From 337a34afd15023df6184edfe8b23e2003cab9f35 Mon Sep 17 00:00:00 2001 From: Bobby Powers Date: Sat, 3 Oct 2026 01:50:32 -0700 Subject: [PATCH] build: run ignored tests in ci and delete xmutil Ignored engine tests ran nowhere, so a pin could go stale unnoticed (the C-LEARN LTM digest has been). A gates cargo profile, scripts/gates.sh and a CI job run every ignored test optimized, with debug assertions on, on every push to main and pull request to main; saying what the test sweeps. The script finds ignored tests itself, so a new gate needs no list entry. The stale digest is skipped by name until its re-pin lands. xmutil goes with its comparison tests: the importer is held to Vensim's own output, and it was the only C++ in the build. Several checks could not fail and now can: test-asan.sh lost its exit status to tee and ran with halt_on_error=0, and the pysimlin tag guard skipped whenever CI fetched no tags. CI also pins its runner image, lints what the hook lints, moves to the Node 24 action majors (setup-uv pinned to a release, since it publishes no major tags), and reports advisories from a scheduled workflow instead of failing unrelated pull requests. --- .github/dependabot.yml | 19 + .github/workflows/advisories.yml | 76 + .github/workflows/ci.yaml | 195 +- .github/workflows/claude-code-review.yml | 4 +- .github/workflows/claude.yml | 4 +- .github/workflows/mcp-release.yml | 39 +- .github/workflows/memory.yml | 34 +- .github/workflows/release.yml | 30 +- .github/workflows/serve-release.yml | 43 +- .github/workflows/ts-release.yml | 32 +- .github/workflows/wasm-opt.yml | 10 +- CLAUDE.md | 1 - Cargo.lock | 26 - Cargo.toml | 18 +- deny.toml | 1 - docs/README.md | 2 +- docs/architecture.md | 13 +- docs/design/engine-performance.md | 2 +- docs/design/mdl-parser.md | 28 +- docs/dev/commands.md | 15 +- docs/dev/rust.md | 79 +- docs/reference/vensim-macros.md | 48 +- docs/tech-debt.md | 22 +- scripts/ci-report-failure.sh | 40 + scripts/dep-policy.json | 5 +- scripts/gates.sh | 48 + scripts/pre-commit | 17 +- src/engine/build.sh | 2 - src/libsimlin/src/lib.rs | 2 +- src/simlin-engine/CLAUDE.md | 9 +- src/simlin-engine/Cargo.toml | 3 - src/simlin-engine/src/compat.rs | 20 +- src/simlin-engine/src/datamodel.rs | 27 +- src/simlin-engine/src/db.rs | 6 +- .../src/db/interned_reuse_tests.rs | 248 ++ src/simlin-engine/src/db/ltm_module_tests.rs | 7 +- src/simlin-engine/src/db/ltm_tests.rs | 10 +- src/simlin-engine/src/lib.rs | 4 - src/simlin-engine/src/mdl/CLAUDE.md | 10 +- .../src/mdl/convert/apply_to_all_tests.rs | 11 +- .../src/mdl/convert/variables.rs | 4 +- src/simlin-engine/src/mdl/mod.rs | 3 +- src/simlin-engine/src/test_common.rs | 28 +- src/simlin-engine/src/test_open_vensim.rs | 37 - src/simlin-engine/src/tools/battery_tests.rs | 3 +- src/simlin-engine/src/tools/corpus_tests.rs | 5 +- src/simlin-engine/src/tools/loops_tests.rs | 5 +- src/simlin-engine/src/tools/outline_tests.rs | 5 +- .../tests/integration/clearn_unit_errors.rs | 2 +- .../integration/ltm_discovery_large_models.rs | 20 +- src/simlin-engine/tests/integration/main.rs | 15 +- .../tests/integration/mdl_equivalence.rs | 1327 -------- .../tests/integration/mdl_roundtrip.rs | 12 +- .../tests/integration/simulate.rs | 43 +- .../tests/integration/simulate_ltm.rs | 8 +- .../tests/integration/simulate_ltm_pinned.rs | 10 +- .../tests/integration/simulate_ltm_wasm.rs | 26 +- .../tests/integration/wrld3_ltm_panic.rs | 5 +- src/simlin-mcp/src/main.rs | 98 +- src/simlin-serve/src/parse.rs | 5 +- src/simlin-serve/tests/integration/smoke.rs | 2 +- src/xmutil/Cargo.toml | 18 - src/xmutil/README.md | 3 - src/xmutil/build.rs | 218 -- src/xmutil/src/lib.rs | 203 -- src/xmutil/third_party/libutf/LICENSE | 13 - src/xmutil/third_party/libutf/NOTICE | 13 - src/xmutil/third_party/libutf/README | 13 - src/xmutil/third_party/libutf/lib9.h | 17 - src/xmutil/third_party/libutf/plan9.h | 31 - src/xmutil/third_party/libutf/portdate | 20 - src/xmutil/third_party/libutf/rune.c | 217 -- src/xmutil/third_party/libutf/runestrcat.c | 25 - src/xmutil/third_party/libutf/runestrchr.c | 35 - src/xmutil/third_party/libutf/runestrcmp.c | 35 - src/xmutil/third_party/libutf/runestrcpy.c | 28 - src/xmutil/third_party/libutf/runestrdup.c | 30 - src/xmutil/third_party/libutf/runestrecpy.c | 32 - src/xmutil/third_party/libutf/runestrlen.c | 24 - src/xmutil/third_party/libutf/runestrncat.c | 32 - src/xmutil/third_party/libutf/runestrncmp.c | 37 - src/xmutil/third_party/libutf/runestrncpy.c | 33 - src/xmutil/third_party/libutf/runestrrchr.c | 30 - src/xmutil/third_party/libutf/runestrstr.c | 44 - src/xmutil/third_party/libutf/runetype.c | 1151 ------- src/xmutil/third_party/libutf/utf.h | 53 - src/xmutil/third_party/libutf/utfdef.h | 35 - src/xmutil/third_party/libutf/utfecpy.c | 38 - src/xmutil/third_party/libutf/utflen.c | 37 - src/xmutil/third_party/libutf/utfnlen.c | 41 - src/xmutil/third_party/libutf/utfrrune.c | 45 - src/xmutil/third_party/libutf/utfrune.c | 44 - src/xmutil/third_party/libutf/utfutf.c | 41 - src/xmutil/third_party/tinyxml2/LICENSE | 18 - src/xmutil/third_party/tinyxml2/tinyxml2.cpp | 3019 ----------------- src/xmutil/third_party/tinyxml2/tinyxml2.h | 2383 ------------- src/xmutil/third_party/xmutil/ContextInfo.cpp | 60 - src/xmutil/third_party/xmutil/ContextInfo.h | 146 - .../third_party/xmutil/Dynamo/DYacc.tab.cpp | 1815 ---------- .../third_party/xmutil/Dynamo/DYacc.tab.hpp | 92 - src/xmutil/third_party/xmutil/Dynamo/DYacc.y | 225 -- .../xmutil/Dynamo/DynamoFunction.cpp | 117 - .../xmutil/Dynamo/DynamoFunction.h | 128 - .../third_party/xmutil/Dynamo/DynamoLex.cpp | 695 ---- .../third_party/xmutil/Dynamo/DynamoLex.h | 68 - .../third_party/xmutil/Dynamo/DynamoParse.cpp | 658 ---- .../third_party/xmutil/Dynamo/DynamoParse.h | 124 - .../xmutil/Dynamo/DynamoParseFunctions.cpp | 89 - .../xmutil/Dynamo/DynamoParseFunctions.h | 31 - .../third_party/xmutil/Function/Function.cpp | 328 -- .../third_party/xmutil/Function/Function.h | 477 --- .../third_party/xmutil/Function/Level.cpp | 7 - .../third_party/xmutil/Function/Level.h | 24 - .../third_party/xmutil/Function/State.cpp | 15 - .../third_party/xmutil/Function/State.h | 99 - .../xmutil/Function/TableFunction.cpp | 40 - .../xmutil/Function/TableFunction.h | 21 - src/xmutil/third_party/xmutil/LICENSE | 21 - src/xmutil/third_party/xmutil/Log.cpp | 39 - src/xmutil/third_party/xmutil/Log.h | 10 - src/xmutil/third_party/xmutil/Model.cpp | 694 ---- src/xmutil/third_party/xmutil/Model.h | 145 - .../third_party/xmutil/Symbol/Equation.cpp | 176 - .../third_party/xmutil/Symbol/Equation.h | 38 - .../third_party/xmutil/Symbol/Expression.cpp | 264 -- .../third_party/xmutil/Symbol/Expression.h | 704 ---- .../xmutil/Symbol/ExpressionList.cpp | 45 - .../xmutil/Symbol/ExpressionList.h | 37 - .../xmutil/Symbol/LeftHandSide.cpp | 34 - .../third_party/xmutil/Symbol/LeftHandSide.h | 35 - .../xmutil/Symbol/NotUsed_SymAllocList.cpp | 30 - .../xmutil/Symbol/NotUsed_SymAllocList.h | 30 - src/xmutil/third_party/xmutil/Symbol/Parse.h | 50 - .../third_party/xmutil/Symbol/Symbol.cpp | 74 - src/xmutil/third_party/xmutil/Symbol/Symbol.h | 74 - .../third_party/xmutil/Symbol/SymbolList.cpp | 113 - .../third_party/xmutil/Symbol/SymbolList.h | 64 - .../xmutil/Symbol/SymbolListList.cpp | 22 - .../xmutil/Symbol/SymbolListList.h | 30 - .../xmutil/Symbol/SymbolNameSpace.cpp | 149 - .../xmutil/Symbol/SymbolNameSpace.h | 44 - .../xmutil/Symbol/SymbolTableBase.cpp | 14 - .../xmutil/Symbol/SymbolTableBase.h | 41 - .../xmutil/Symbol/UnitExpression.cpp | 82 - .../xmutil/Symbol/UnitExpression.h | 30 - .../third_party/xmutil/Symbol/Units.cpp | 12 - src/xmutil/third_party/xmutil/Symbol/Units.h | 21 - .../third_party/xmutil/Symbol/Variable.cpp | 514 --- .../third_party/xmutil/Symbol/Variable.h | 376 -- src/xmutil/third_party/xmutil/Unicode.cpp | 37 - src/xmutil/third_party/xmutil/Unicode.h | 13 - src/xmutil/third_party/xmutil/Vensim/VLex.y | 57 - .../third_party/xmutil/Vensim/VYacc.tab.cpp | 2099 ------------ .../third_party/xmutil/Vensim/VYacc.tab.hpp | 99 - src/xmutil/third_party/xmutil/Vensim/VYacc.y | 266 -- .../third_party/xmutil/Vensim/VensimLex.cpp | 697 ---- .../third_party/xmutil/Vensim/VensimLex.h | 67 - .../third_party/xmutil/Vensim/VensimParse.cpp | 701 ---- .../third_party/xmutil/Vensim/VensimParse.h | 120 - .../xmutil/Vensim/VensimParseFunctions.cpp | 86 - .../xmutil/Vensim/VensimParseFunctions.h | 30 - .../third_party/xmutil/Vensim/VensimView.cpp | 451 --- .../third_party/xmutil/Vensim/VensimView.h | 177 - src/xmutil/third_party/xmutil/XMUtil.cpp | 341 -- src/xmutil/third_party/xmutil/XMUtil.h | 86 - .../xmutil/Xmile/XMILEGenerator.cpp | 1130 ------ .../third_party/xmutil/Xmile/XMILEGenerator.h | 45 - test-asan.sh | 35 +- 168 files changed, 1026 insertions(+), 25711 deletions(-) create mode 100644 .github/dependabot.yml create mode 100644 .github/workflows/advisories.yml create mode 100755 scripts/ci-report-failure.sh create mode 100755 scripts/gates.sh create mode 100644 src/simlin-engine/src/db/interned_reuse_tests.rs delete mode 100644 src/simlin-engine/src/test_open_vensim.rs delete mode 100644 src/simlin-engine/tests/integration/mdl_equivalence.rs delete mode 100644 src/xmutil/Cargo.toml delete mode 100644 src/xmutil/README.md delete mode 100644 src/xmutil/build.rs delete mode 100644 src/xmutil/src/lib.rs delete mode 100644 src/xmutil/third_party/libutf/LICENSE delete mode 100644 src/xmutil/third_party/libutf/NOTICE delete mode 100644 src/xmutil/third_party/libutf/README delete mode 100644 src/xmutil/third_party/libutf/lib9.h delete mode 100644 src/xmutil/third_party/libutf/plan9.h delete mode 100644 src/xmutil/third_party/libutf/portdate delete mode 100644 src/xmutil/third_party/libutf/rune.c delete mode 100644 src/xmutil/third_party/libutf/runestrcat.c delete mode 100644 src/xmutil/third_party/libutf/runestrchr.c delete mode 100644 src/xmutil/third_party/libutf/runestrcmp.c delete mode 100644 src/xmutil/third_party/libutf/runestrcpy.c delete mode 100644 src/xmutil/third_party/libutf/runestrdup.c delete mode 100644 src/xmutil/third_party/libutf/runestrecpy.c delete mode 100644 src/xmutil/third_party/libutf/runestrlen.c delete mode 100644 src/xmutil/third_party/libutf/runestrncat.c delete mode 100644 src/xmutil/third_party/libutf/runestrncmp.c delete mode 100644 src/xmutil/third_party/libutf/runestrncpy.c delete mode 100644 src/xmutil/third_party/libutf/runestrrchr.c delete mode 100644 src/xmutil/third_party/libutf/runestrstr.c delete mode 100644 src/xmutil/third_party/libutf/runetype.c delete mode 100644 src/xmutil/third_party/libutf/utf.h delete mode 100644 src/xmutil/third_party/libutf/utfdef.h delete mode 100644 src/xmutil/third_party/libutf/utfecpy.c delete mode 100644 src/xmutil/third_party/libutf/utflen.c delete mode 100644 src/xmutil/third_party/libutf/utfnlen.c delete mode 100644 src/xmutil/third_party/libutf/utfrrune.c delete mode 100644 src/xmutil/third_party/libutf/utfrune.c delete mode 100644 src/xmutil/third_party/libutf/utfutf.c delete mode 100644 src/xmutil/third_party/tinyxml2/LICENSE delete mode 100755 src/xmutil/third_party/tinyxml2/tinyxml2.cpp delete mode 100755 src/xmutil/third_party/tinyxml2/tinyxml2.h delete mode 100644 src/xmutil/third_party/xmutil/ContextInfo.cpp delete mode 100644 src/xmutil/third_party/xmutil/ContextInfo.h delete mode 100644 src/xmutil/third_party/xmutil/Dynamo/DYacc.tab.cpp delete mode 100644 src/xmutil/third_party/xmutil/Dynamo/DYacc.tab.hpp delete mode 100644 src/xmutil/third_party/xmutil/Dynamo/DYacc.y delete mode 100644 src/xmutil/third_party/xmutil/Dynamo/DynamoFunction.cpp delete mode 100644 src/xmutil/third_party/xmutil/Dynamo/DynamoFunction.h delete mode 100644 src/xmutil/third_party/xmutil/Dynamo/DynamoLex.cpp delete mode 100644 src/xmutil/third_party/xmutil/Dynamo/DynamoLex.h delete mode 100644 src/xmutil/third_party/xmutil/Dynamo/DynamoParse.cpp delete mode 100644 src/xmutil/third_party/xmutil/Dynamo/DynamoParse.h delete mode 100644 src/xmutil/third_party/xmutil/Dynamo/DynamoParseFunctions.cpp delete mode 100644 src/xmutil/third_party/xmutil/Dynamo/DynamoParseFunctions.h delete mode 100644 src/xmutil/third_party/xmutil/Function/Function.cpp delete mode 100644 src/xmutil/third_party/xmutil/Function/Function.h delete mode 100644 src/xmutil/third_party/xmutil/Function/Level.cpp delete mode 100644 src/xmutil/third_party/xmutil/Function/Level.h delete mode 100644 src/xmutil/third_party/xmutil/Function/State.cpp delete mode 100644 src/xmutil/third_party/xmutil/Function/State.h delete mode 100644 src/xmutil/third_party/xmutil/Function/TableFunction.cpp delete mode 100644 src/xmutil/third_party/xmutil/Function/TableFunction.h delete mode 100644 src/xmutil/third_party/xmutil/LICENSE delete mode 100644 src/xmutil/third_party/xmutil/Log.cpp delete mode 100644 src/xmutil/third_party/xmutil/Log.h delete mode 100644 src/xmutil/third_party/xmutil/Model.cpp delete mode 100644 src/xmutil/third_party/xmutil/Model.h delete mode 100644 src/xmutil/third_party/xmutil/Symbol/Equation.cpp delete mode 100644 src/xmutil/third_party/xmutil/Symbol/Equation.h delete mode 100644 src/xmutil/third_party/xmutil/Symbol/Expression.cpp delete mode 100644 src/xmutil/third_party/xmutil/Symbol/Expression.h delete mode 100644 src/xmutil/third_party/xmutil/Symbol/ExpressionList.cpp delete mode 100644 src/xmutil/third_party/xmutil/Symbol/ExpressionList.h delete mode 100644 src/xmutil/third_party/xmutil/Symbol/LeftHandSide.cpp delete mode 100644 src/xmutil/third_party/xmutil/Symbol/LeftHandSide.h delete mode 100644 src/xmutil/third_party/xmutil/Symbol/NotUsed_SymAllocList.cpp delete mode 100644 src/xmutil/third_party/xmutil/Symbol/NotUsed_SymAllocList.h delete mode 100644 src/xmutil/third_party/xmutil/Symbol/Parse.h delete mode 100644 src/xmutil/third_party/xmutil/Symbol/Symbol.cpp delete mode 100644 src/xmutil/third_party/xmutil/Symbol/Symbol.h delete mode 100644 src/xmutil/third_party/xmutil/Symbol/SymbolList.cpp delete mode 100644 src/xmutil/third_party/xmutil/Symbol/SymbolList.h delete mode 100644 src/xmutil/third_party/xmutil/Symbol/SymbolListList.cpp delete mode 100644 src/xmutil/third_party/xmutil/Symbol/SymbolListList.h delete mode 100644 src/xmutil/third_party/xmutil/Symbol/SymbolNameSpace.cpp delete mode 100644 src/xmutil/third_party/xmutil/Symbol/SymbolNameSpace.h delete mode 100644 src/xmutil/third_party/xmutil/Symbol/SymbolTableBase.cpp delete mode 100644 src/xmutil/third_party/xmutil/Symbol/SymbolTableBase.h delete mode 100644 src/xmutil/third_party/xmutil/Symbol/UnitExpression.cpp delete mode 100644 src/xmutil/third_party/xmutil/Symbol/UnitExpression.h delete mode 100644 src/xmutil/third_party/xmutil/Symbol/Units.cpp delete mode 100644 src/xmutil/third_party/xmutil/Symbol/Units.h delete mode 100644 src/xmutil/third_party/xmutil/Symbol/Variable.cpp delete mode 100644 src/xmutil/third_party/xmutil/Symbol/Variable.h delete mode 100644 src/xmutil/third_party/xmutil/Unicode.cpp delete mode 100644 src/xmutil/third_party/xmutil/Unicode.h delete mode 100644 src/xmutil/third_party/xmutil/Vensim/VLex.y delete mode 100644 src/xmutil/third_party/xmutil/Vensim/VYacc.tab.cpp delete mode 100644 src/xmutil/third_party/xmutil/Vensim/VYacc.tab.hpp delete mode 100644 src/xmutil/third_party/xmutil/Vensim/VYacc.y delete mode 100644 src/xmutil/third_party/xmutil/Vensim/VensimLex.cpp delete mode 100644 src/xmutil/third_party/xmutil/Vensim/VensimLex.h delete mode 100644 src/xmutil/third_party/xmutil/Vensim/VensimParse.cpp delete mode 100644 src/xmutil/third_party/xmutil/Vensim/VensimParse.h delete mode 100644 src/xmutil/third_party/xmutil/Vensim/VensimParseFunctions.cpp delete mode 100644 src/xmutil/third_party/xmutil/Vensim/VensimParseFunctions.h delete mode 100644 src/xmutil/third_party/xmutil/Vensim/VensimView.cpp delete mode 100644 src/xmutil/third_party/xmutil/Vensim/VensimView.h delete mode 100644 src/xmutil/third_party/xmutil/XMUtil.cpp delete mode 100644 src/xmutil/third_party/xmutil/XMUtil.h delete mode 100644 src/xmutil/third_party/xmutil/Xmile/XMILEGenerator.cpp delete mode 100644 src/xmutil/third_party/xmutil/Xmile/XMILEGenerator.h diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 000000000..dca26f03a --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,19 @@ +--- +# Action versions only. The workflows name each action by its major tag, and a +# major is retired on GitHub's schedule (the Node 20 runtime was), not ours: +# one grouped pull request a month keeps them current, so a retirement is never +# the first time anyone looks at them. +# +# Cargo and npm dependencies are not here on purpose. Cargo advisories are +# checked daily by .github/workflows/advisories.yml, and routine version bumps +# of either ecosystem are made by hand, with the lockfile reviewed. +version: 2 +updates: + - package-ecosystem: github-actions + directory: / + schedule: + interval: monthly + groups: + actions: + patterns: + - "*" diff --git a/.github/workflows/advisories.yml b/.github/workflows/advisories.yml new file mode 100644 index 000000000..7b7bbe7ef --- /dev/null +++ b/.github/workflows/advisories.yml @@ -0,0 +1,76 @@ +--- +# `cargo deny check advisories`: the one cargo-deny check whose answer changes +# while the tree does not. A RUSTSEC entry published overnight (or a crate +# yanked, which `yanked = "deny"` in deny.toml files under this check) fails a +# commit that passed the day before, so in `ci.yaml` it turned `main` and every +# open pull request red for a reason none of them caused. The checks that ARE a +# function of the tree -- bans, licenses, sources -- stay in ci.yaml's lint job. +# +# This runs where an advisory can actually arrive: +# +# * daily, against `main`: a new advisory opens (or adds to) an issue through +# scripts/ci-report-failure.sh, because a scheduled run has no author to +# notify; +# * on any change to the lockfile or the policy, where a failure belongs to the +# change: that pull request is the one adding the affected dependency or +# dropping the ignore entry. +# +# Do NOT make this a required status check: it is path-filtered, so it reports +# nothing on a pull request that touches none of these paths, and a required +# check that never reports blocks the pull request forever. +name: Dependency advisories + +"on": + schedule: + - cron: '23 5 * * *' + workflow_dispatch: + push: + branches: + - main + paths: + - 'Cargo.lock' + - 'deny.toml' + - '.github/workflows/advisories.yml' + pull_request: + branches: + - main + paths: + - 'Cargo.lock' + - 'deny.toml' + - '.github/workflows/advisories.yml' + +permissions: + contents: read + +jobs: + advisories: + name: cargo-deny advisories + runs-on: ubuntu-24.04 + timeout-minutes: 10 + permissions: + contents: read + issues: write + steps: + - name: Checkout repository + uses: actions/checkout@v7 + + - name: Install cargo-deny + uses: taiki-e/install-action@v2 + with: + tool: cargo-deny + + # No pipe into `tee`: the step's status has to be cargo-deny's, and a + # pipeline reports its last command's. + - name: Check advisories + shell: bash + run: | + status=0 + cargo deny check advisories > advisories.txt 2>&1 || status=$? + cat advisories.txt + exit "$status" + + - name: Report a scheduled failure + if: failure() && github.event_name == 'schedule' + env: + GH_TOKEN: ${{ github.token }} + run: bash scripts/ci-report-failure.sh "cargo-deny reports an advisory against main" advisories.txt diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index da668bf5a..26693b06c 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -28,24 +28,33 @@ jobs: strategy: fail-fast: false matrix: - os: [ubuntu-latest, macos-latest] + os: [ubuntu-24.04, macos-latest] env: RUSTFLAGS: -D warnings RUST_BACKTRACE: 1 steps: - name: Checkout repository - uses: actions/checkout@v4 - with: - # simlin-mcp's pysimlin_version_matches_latest_tag needs the - # pysimlin-v* tags (setuptools-scm makes the tag the version's - # source of truth); without them the guard skips on every run. - fetch-tags: true + uses: actions/checkout@v7 + + # simlin-mcp's pysimlin_version_matches_latest_tag reads the + # pysimlin-v* tags (setuptools-scm makes the tag the version's + # source of truth) and skips when it sees none. The checkout is + # one commit deep and `fetch-tags: true` adds no tag refspec to + # that fetch (the step's own log line is `git fetch --depth=1 + # origin +:refs/remotes/origin/main`), so the tags are + # fetched by name here; the step fails if there are none, which + # is what keeps the guard from skipping on every run. + - name: Fetch the pysimlin release tags + shell: bash + run: | + git fetch --depth=1 origin 'refs/tags/pysimlin-v*:refs/tags/pysimlin-v*' + test -n "$(git tag --list 'pysimlin-v*')" - name: Install Rust toolchain run: rustup show - name: Cache cargo registry and target - uses: actions/cache@v4 + uses: actions/cache@v6 with: path: | ~/.cargo/registry/index/ @@ -62,11 +71,14 @@ jobs: - name: Compile tests run: cargo test --workspace --no-run - # 3-minute wall-clock cap on `cargo test` (post-compile). CI - # baseline on ubuntu-latest is ~60s; the cap is a ~3x safety - # rail against the kind of regression that made PR #461's job + # 3-minute wall-clock cap on `cargo test` (post-compile). On a + # 4-core runner the step takes 56 to 127 s for the same suite + # (the spread is the runner, not the commit), so the cap is + # 1.4x the slow end: a rail + # against the kind of regression that made PR #461's job # hit GitHub's 60-minute per-job cap (one test binary there - # took 44 minutes). Uses the step-level `timeout-minutes` + # took 44 minutes), with little room for the suite to grow. + # Uses the step-level `timeout-minutes` # field rather than coreutils `timeout(1)` so we do not need # to special-case macos-latest (which has no `timeout` on # PATH). Raise in lockstep with scripts/pre-commit if the @@ -78,19 +90,19 @@ jobs: rust: name: Lint and format Rust - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 env: RUSTFLAGS: -D warnings RUST_BACKTRACE: 1 steps: - name: Checkout repository - uses: actions/checkout@v4 + uses: actions/checkout@v7 - name: Install Rust toolchain run: rustup show - name: Cache cargo registry and target - uses: actions/cache@v4 + uses: actions/cache@v6 with: path: | ~/.cargo/registry/index/ @@ -104,32 +116,96 @@ jobs: - name: Check formatting run: cargo fmt -- --check --color=auto + # The same invocation scripts/pre-commit runs. Without `--all-targets` + # no test, example or bench is linted, and without `--all-features` no + # code behind `ext_data` or `ltm_bench` is even compiled, so + # a change that never went through the hook would be held to less here + # than a local commit is. - name: Lint with Clippy - run: cargo clippy --workspace -- -D warnings + run: cargo clippy --workspace --all-targets --all-features -- -D warnings - name: Install cargo-deny uses: taiki-e/install-action@v2 with: tool: cargo-deny - - name: Run cargo-deny - run: cargo deny check + # The checks that are a function of this tree alone. `advisories` is + # not: a RUSTSEC entry published tomorrow fails the same commit that + # passed today, so it runs in .github/workflows/advisories.yml (daily, + # and on every change to Cargo.lock or deny.toml), which opens an issue + # rather than turning an unrelated pull request red. + - name: Run cargo-deny (bans, licenses, sources) + run: cargo deny check bans licenses sources + + # The gates: every test the Build job cannot run. A test is `#[ignore]`d + # when a debug build takes too long over it (a sweep of the model corpus, + # C-LEARN or World3 compiled and run), so `cargo test` and the pre-commit + # hook skip it and this job is the only thing that holds what it pins. An + # ignored test nothing runs goes stale silently: a pin a later change moved + # is found by whoever next runs it by hand, long after the change. + # + # So `#[ignore]` means exactly "needs an optimized build", and every ignored + # test has to pass here. A test that records a known defect is not ignored: + # it asserts the defect and runs in the Build job, so it fails when the + # defect is fixed. + # + # scripts/gates.sh is the one statement of what runs (the packages, the + # features, the `gates` cargo profile: optimized, with debug assertions and + # overflow checks) and is what a developer runs too. It runs the rest of + # each suite as well, with `ext_data` on: the only run of the suite with the + # Excel data provider compiled in. + # + # The run is about two minutes on four desktop cores (5 CPU-minutes); the + # caps leave room for several times that, since corpus sweeps are added + # here rather than to the capped debug suite. + gates: + name: Gates (ignored tests, optimized) + runs-on: ubuntu-24.04 + timeout-minutes: 60 + env: + RUSTFLAGS: -D warnings + RUST_BACKTRACE: 1 + steps: + - name: Checkout repository + uses: actions/checkout@v7 + + - name: Install Rust toolchain + run: rustup show + + - name: Cache cargo registry and target + uses: actions/cache@v6 + with: + path: | + ~/.cargo/registry/index/ + ~/.cargo/registry/cache/ + ~/.cargo/git/db/ + target/ + key: cargo-gates-${{ hashFiles('**/Cargo.lock', 'Cargo.toml') }} + restore-keys: | + cargo-gates- + + - name: Compile the gates + run: scripts/gates.sh --no-run + + - name: Run the gates + timeout-minutes: 25 + run: scripts/gates.sh coverage: name: Code Coverage - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 env: RUSTFLAGS: -D warnings RUST_BACKTRACE: 1 steps: - name: Checkout repository - uses: actions/checkout@v4 + uses: actions/checkout@v7 - name: Install Rust toolchain run: rustup show - name: Cache cargo registry and target - uses: actions/cache@v4 + uses: actions/cache@v6 with: path: | ~/.cargo/registry/index/ @@ -140,6 +216,13 @@ jobs: restore-keys: | cargo-llvm-cov- + # The suite's pysimlin_version_matches_latest_tag needs the release + # tags, as in the Build job. + - name: Fetch the pysimlin release tags + run: | + git fetch --depth=1 origin 'refs/tags/pysimlin-v*:refs/tags/pysimlin-v*' + test -n "$(git tag --list 'pysimlin-v*')" + - name: Install cargo-llvm-cov uses: taiki-e/install-action@v2 with: @@ -157,8 +240,8 @@ jobs: # suite 5-50x slower than native, first blowing a 90-minute cap # and later violating timing assumptions in libsimlin's # concurrency tests. llvm-cov instrumentation runs tests at - # near-native speed (native `cargo test --workspace` is ~60s - # post-compile), so this cap is a generous backstop dominated by + # near-native speed (native `cargo test --workspace` is one to + # two minutes post-compile), so this cap is a generous backstop dominated by # the instrumented build: a warm-cache run fits comfortably in # 30 minutes, and the headroom above that absorbs the cold-cache # case (first run after a Cargo.lock change or cache eviction @@ -172,22 +255,22 @@ jobs: run: cargo llvm-cov --workspace --lcov --output-path lcov.info - name: Upload Coverage - uses: codecov/codecov-action@v5 + uses: codecov/codecov-action@v7 with: token: ${{ secrets.CODECOV_TOKEN }} files: lcov.info frontend: - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 steps: - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v7 - name: Install pnpm - uses: pnpm/action-setup@v4 + uses: pnpm/action-setup@v6 - name: Install node - uses: actions/setup-node@v4 + uses: actions/setup-node@v7 with: node-version: '22' cache: 'pnpm' @@ -196,7 +279,7 @@ jobs: run: rustup show - name: Cache cargo registry and target - uses: actions/cache@v4 + uses: actions/cache@v6 with: path: | ~/.cargo/registry/index/ @@ -208,7 +291,7 @@ jobs: cargo-wasm- - name: Install Python - uses: actions/setup-python@v5 + uses: actions/setup-python@v7 with: python-version: '3.12' @@ -255,18 +338,18 @@ jobs: pysimlin: name: pysimlin type check and tests - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 env: RUST_BACKTRACE: 1 steps: - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v7 - name: Install Rust toolchain run: rustup show - name: Cache cargo registry and target - uses: actions/cache@v4 + uses: actions/cache@v6 with: path: | ~/.cargo/registry/index/ @@ -278,7 +361,7 @@ jobs: cargo-pysimlin- - name: Install uv - uses: astral-sh/setup-uv@v5 + uses: astral-sh/setup-uv@v10.2.0 with: enable-cache: true @@ -320,22 +403,22 @@ jobs: RUST_BACKTRACE: 1 steps: - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v7 - name: Install Rust toolchain run: rustup show - name: Install pnpm - uses: pnpm/action-setup@v4 + uses: pnpm/action-setup@v6 - name: Install node - uses: actions/setup-node@v4 + uses: actions/setup-node@v7 with: node-version: '22' cache: 'pnpm' - name: Cache cargo registry and target - uses: actions/cache@v4 + uses: actions/cache@v6 with: path: | ~/.cargo/registry/index/ @@ -347,7 +430,7 @@ jobs: cargo-pysimlin-e2e- - name: Install uv - uses: astral-sh/setup-uv@v5 + uses: astral-sh/setup-uv@v10.2.0 with: enable-cache: true @@ -394,13 +477,13 @@ jobs: # Screenshots, the trace, and the jupyter server log are what a failed # journey needs to be understood. They live in a dot-directory, and - # upload-artifact@v4 skips hidden files unless told otherwise (it then + # upload-artifact skips hidden files unless told otherwise (it then # finds nothing at all). Nothing to upload is a warning, not an error: # this step also runs when an earlier build step failed, before the # journey wrote anything. - name: Upload journey output if: failure() - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v7 with: name: pysimlin-e2e-output path: src/pysimlin/e2e/.output/ @@ -409,16 +492,16 @@ jobs: policy: name: Policy checks - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 steps: - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v7 - name: Install ripgrep run: sudo apt-get install -y ripgrep - name: Install Python - uses: actions/setup-python@v5 + uses: actions/setup-python@v7 with: python-version: '3.12' @@ -438,20 +521,20 @@ jobs: # the Windows leg past its cap (GH #1054). serve-web: name: simlin-serve embedded SPA - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 timeout-minutes: 20 steps: - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v7 - name: Install Rust toolchain run: rustup show - name: Install pnpm - uses: pnpm/action-setup@v4 + uses: pnpm/action-setup@v6 - name: Install node - uses: actions/setup-node@v4 + uses: actions/setup-node@v7 with: node-version: '24' cache: 'pnpm' @@ -459,7 +542,7 @@ jobs: # Same key family as the frontend job: both build the same gate-profile # wasm engine into the same target directories, so they share one cache. - name: Cache cargo registry and target - uses: actions/cache@v4 + uses: actions/cache@v6 with: path: | ~/.cargo/registry/index/ @@ -489,7 +572,7 @@ jobs: run: pnpm --filter "@simlin/serve-web..." run build - name: Upload the SPA for the smoke legs - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v7 with: name: serve-web-dist path: src/simlin-serve/web/dist @@ -516,18 +599,18 @@ jobs: # npm matrix (serve-release.yml) additionally ships linux-arm64 and # win32-x64, both cross-compiled; neither is a GitHub-hosted runner # option, and linux-x64 covers the Linux runtime path. - os: [ubuntu-latest, macos-latest] + os: [ubuntu-24.04, macos-latest] env: RUST_BACKTRACE: 1 steps: - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v7 - name: Install Rust toolchain run: rustup show - name: Cache cargo registry and target - uses: actions/cache@v4 + uses: actions/cache@v6 with: path: | ~/.cargo/registry/index/ @@ -542,7 +625,7 @@ jobs: # test spawns embeds this SPA. The smoke test asserts the entry point is # actually served, which is what catches a missing or empty embed. - name: Download the embedded SPA - uses: actions/download-artifact@v4 + uses: actions/download-artifact@v8 with: name: serve-web-dist path: src/simlin-serve/web/dist @@ -574,13 +657,13 @@ jobs: RUST_BACKTRACE: 1 steps: - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v7 - name: Install Rust toolchain run: rustup show - name: Cache cargo registry and target - uses: actions/cache@v4 + uses: actions/cache@v6 with: path: | ~/.cargo/registry/index/ diff --git a/.github/workflows/claude-code-review.yml b/.github/workflows/claude-code-review.yml index b7164ac82..e21caa820 100644 --- a/.github/workflows/claude-code-review.yml +++ b/.github/workflows/claude-code-review.yml @@ -21,7 +21,7 @@ jobs: # github.event.pull_request.user.login == 'new-developer' || # github.event.pull_request.author_association == 'FIRST_TIME_CONTRIBUTOR' - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 permissions: contents: read pull-requests: read @@ -30,7 +30,7 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v4 + uses: actions/checkout@v7 with: fetch-depth: 1 diff --git a/.github/workflows/claude.yml b/.github/workflows/claude.yml index ae2362b71..f28fe1c7e 100644 --- a/.github/workflows/claude.yml +++ b/.github/workflows/claude.yml @@ -20,7 +20,7 @@ jobs: (github.event_name == 'pull_request_review_comment' && contains(github.event.comment.body, '@claude')) || (github.event_name == 'pull_request_review' && contains(github.event.review.body, '@claude')) || (github.event_name == 'issues' && (contains(github.event.issue.body, '@claude') || contains(github.event.issue.title, '@claude'))) - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 permissions: contents: read pull-requests: read @@ -29,7 +29,7 @@ jobs: actions: read # Required for Claude to read CI results on PRs steps: - name: Checkout repository - uses: actions/checkout@v4 + uses: actions/checkout@v7 with: fetch-depth: 1 diff --git a/.github/workflows/mcp-release.yml b/.github/workflows/mcp-release.yml index dc929e80a..f58443a16 100644 --- a/.github/workflows/mcp-release.yml +++ b/.github/workflows/mcp-release.yml @@ -9,14 +9,19 @@ on: permissions: contents: read +# mlugg/setup-zig's latest release (v2.2.1) still declares the Node 20 runtime +# GitHub is retiring; every other action here is Node 24 already. +env: + FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true + jobs: validate: name: Validate version - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 outputs: version: ${{ steps.version.outputs.version }} steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v7 - name: Extract and validate version id: version @@ -44,17 +49,17 @@ jobs: matrix: include: - target: x86_64-unknown-linux-musl - os: ubuntu-latest + os: ubuntu-24.04 artifact: mcp-linux-x64 binary: simlin-mcp use-zigbuild: true - target: aarch64-unknown-linux-musl - os: ubuntu-latest + os: ubuntu-24.04 artifact: mcp-linux-arm64 binary: simlin-mcp use-zigbuild: true - target: x86_64-pc-windows-gnu - os: ubuntu-latest + os: ubuntu-24.04 artifact: mcp-win32-x64 binary: simlin-mcp.exe use-zigbuild: true @@ -64,7 +69,7 @@ jobs: binary: simlin-mcp use-zigbuild: false steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v7 - name: Install Rust toolchain run: | @@ -72,7 +77,7 @@ jobs: rustup target add ${{ matrix.target }} - name: Cache Cargo artifacts - uses: actions/cache@v4 + uses: actions/cache@v6 with: path: | ~/.cargo/bin @@ -105,7 +110,7 @@ jobs: run: cargo build -p simlin-mcp --locked --release --target ${{ matrix.target }} - name: Upload binary - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v7 with: name: ${{ matrix.artifact }} path: target/${{ matrix.target }}/release/${{ matrix.binary }} @@ -115,23 +120,23 @@ jobs: publish-platform: name: Publish platform packages needs: [validate, build] - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 if: startsWith(github.ref, 'refs/tags/mcp-v') permissions: contents: read id-token: write steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v7 - - uses: pnpm/action-setup@v4 + - uses: pnpm/action-setup@v6 # Intentionally omit registry-url: it injects a token-based .npmrc that # conflicts with npm's OIDC provenance flow. Trusted publishing handles auth. - - uses: actions/setup-node@v4 + - uses: actions/setup-node@v7 with: node-version: '24' - - uses: actions/download-artifact@v4 + - uses: actions/download-artifact@v8 with: path: artifacts/ @@ -200,18 +205,18 @@ jobs: publish-wrapper: name: Publish @simlin/mcp needs: [validate, publish-platform] - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 if: startsWith(github.ref, 'refs/tags/mcp-v') permissions: contents: read id-token: write steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v7 - - uses: pnpm/action-setup@v4 + - uses: pnpm/action-setup@v6 # Intentionally omit registry-url: trusted publishing handles auth (see publish-platform) - - uses: actions/setup-node@v4 + - uses: actions/setup-node@v7 with: node-version: '24' diff --git a/.github/workflows/memory.yml b/.github/workflows/memory.yml index 2a37fcb2f..f3d7870d7 100644 --- a/.github/workflows/memory.yml +++ b/.github/workflows/memory.yml @@ -12,21 +12,27 @@ on: env: FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true +# `issues: write` is for the scheduled run alone, which has no author to tell +# that it failed and files an issue instead (scripts/ci-report-failure.sh). +permissions: + contents: read + issues: write + jobs: asan-pysimlin: name: AddressSanitizer Testing (pysimlin) - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v7 - name: Set up Python - uses: actions/setup-python@v5 + uses: actions/setup-python@v7 with: python-version: '3.13' - name: Install uv - uses: astral-sh/setup-uv@v5 + uses: astral-sh/setup-uv@v10.2.0 with: enable-cache: true @@ -63,7 +69,9 @@ jobs: # Find the AddressSanitizer runtime library ASAN_LIB=$(gcc -print-file-name=libasan.so) - # Run tests with verbose ASAN output and timeout + # Run tests with verbose ASAN output and timeout. With halt_on_error=0 + # a leak here is a line in the log, not a failure: CPython's own + # exit-time leaks would otherwise turn every run red. RUST_BACKTRACE=1 \ ASAN_OPTIONS="detect_leaks=1:check_initialization_order=1:strict_init_order=1:verbosity=1:print_stats=1:halt_on_error=0:print_module_map=1:log_path=/tmp/asan.log" \ LD_PRELOAD=$ASAN_LIB \ @@ -88,12 +96,18 @@ jobs: exit ${EXIT_CODE:-0} + - name: Report a scheduled failure + if: failure() && github.event_name == 'schedule' + env: + GH_TOKEN: ${{ github.token }} + run: bash scripts/ci-report-failure.sh "The scheduled AddressSanitizer run of pysimlin fails on main" + asan-full: name: AddressSanitizer Testing (Full Rust Suite) - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v7 - name: Set up Rust uses: actions-rust-lang/setup-rust-toolchain@v1 @@ -103,3 +117,9 @@ jobs: - name: Run ASAN tests run: ./test-asan.sh + + - name: Report a scheduled failure + if: failure() && github.event_name == 'schedule' + env: + GH_TOKEN: ${{ github.token }} + run: bash scripts/ci-report-failure.sh "The scheduled AddressSanitizer run of the Rust suite fails on main" asan-test.log diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index a61da1a82..fb43602e7 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -18,7 +18,7 @@ jobs: name: Build widget assets runs-on: ubuntu-24.04 steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v7 - name: Install Rust toolchain run: rustup show @@ -30,16 +30,16 @@ jobs: run: ./scripts/install-binaryen.sh - name: Install pnpm - uses: pnpm/action-setup@v4 + uses: pnpm/action-setup@v6 - name: Install node - uses: actions/setup-node@v4 + uses: actions/setup-node@v7 with: node-version: '22' cache: 'pnpm' - name: Cache cargo registry and target - uses: actions/cache@v4 + uses: actions/cache@v6 with: path: | ~/.cargo/registry/index/ @@ -61,7 +61,7 @@ jobs: run: python3 src/pysimlin/scripts/stage_widget_assets.py --require-opt - name: Upload widget assets - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v7 with: name: widget-assets path: | @@ -79,14 +79,14 @@ jobs: os: [ubuntu-24.04, macos-14] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v7 # The staged assets land where setup.py's package-data expects them; # the check re-verifies them (present, non-empty, hashes match the # manifest, wasm-opt'd) before any wheel is built, so a broken # artifact fails here rather than as a missing file in the wheel. - name: Download widget assets - uses: actions/download-artifact@v4 + uses: actions/download-artifact@v8 with: name: widget-assets path: src/pysimlin/simlin/_widget @@ -96,12 +96,12 @@ jobs: - name: Set up QEMU if: runner.os == 'Linux' - uses: docker/setup-qemu-action@v3 + uses: docker/setup-qemu-action@v4 with: platforms: arm64 - name: Set up Python - uses: actions/setup-python@v5 + uses: actions/setup-python@v7 with: python-version: '3.13' @@ -144,7 +144,7 @@ jobs: package-dir: src/pysimlin - name: Upload wheels - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v7 with: name: wheels-${{ matrix.os }} path: wheelhouse/*.whl @@ -159,20 +159,20 @@ jobs: python-version: ['3.11', '3.12', '3.13', '3.14'] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v7 - name: Set up Python - uses: actions/setup-python@v5 + uses: actions/setup-python@v7 with: python-version: ${{ matrix.python-version }} - name: Install uv - uses: astral-sh/setup-uv@v5 + uses: astral-sh/setup-uv@v10.2.0 with: enable-cache: true - name: Download wheels - uses: actions/download-artifact@v4 + uses: actions/download-artifact@v8 with: pattern: wheels-* path: wheelhouse @@ -259,7 +259,7 @@ jobs: steps: - name: Download wheels - uses: actions/download-artifact@v4 + uses: actions/download-artifact@v8 with: pattern: wheels-* path: wheelhouse diff --git a/.github/workflows/serve-release.yml b/.github/workflows/serve-release.yml index b060166a9..33a8a4661 100644 --- a/.github/workflows/serve-release.yml +++ b/.github/workflows/serve-release.yml @@ -9,14 +9,19 @@ on: permissions: contents: read +# mlugg/setup-zig's latest release (v2.2.1) still declares the Node 20 runtime +# GitHub is retiring; every other action here is Node 24 already. +env: + FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true + jobs: validate: name: Validate version - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 outputs: version: ${{ steps.version.outputs.version }} steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v7 - name: Extract and validate version id: version @@ -44,17 +49,17 @@ jobs: matrix: include: - target: x86_64-unknown-linux-musl - os: ubuntu-latest + os: ubuntu-24.04 artifact: serve-linux-x64 binary: simlin-serve use-zigbuild: true - target: aarch64-unknown-linux-musl - os: ubuntu-latest + os: ubuntu-24.04 artifact: serve-linux-arm64 binary: simlin-serve use-zigbuild: true - target: x86_64-pc-windows-gnu - os: ubuntu-latest + os: ubuntu-24.04 artifact: serve-win32-x64 binary: simlin-serve.exe use-zigbuild: true @@ -64,7 +69,7 @@ jobs: binary: simlin-serve use-zigbuild: false steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v7 - name: Install Rust toolchain run: | @@ -72,7 +77,7 @@ jobs: rustup target add ${{ matrix.target }} - name: Cache Cargo artifacts - uses: actions/cache@v4 + uses: actions/cache@v6 with: path: | ~/.cargo/bin @@ -85,9 +90,9 @@ jobs: restore-keys: | cargo-serve-${{ matrix.os }}-${{ matrix.target }}- - - uses: pnpm/action-setup@v4 + - uses: pnpm/action-setup@v6 - - uses: actions/setup-node@v4 + - uses: actions/setup-node@v7 with: node-version: '24' @@ -118,7 +123,7 @@ jobs: run: cargo build -p simlin-serve --locked --release --target ${{ matrix.target }} - name: Upload binary - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v7 with: name: ${{ matrix.artifact }} path: target/${{ matrix.target }}/release/${{ matrix.binary }} @@ -128,23 +133,23 @@ jobs: publish-platform: name: Publish platform packages needs: [validate, build] - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 if: startsWith(github.ref, 'refs/tags/serve-v') permissions: contents: read id-token: write steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v7 - - uses: pnpm/action-setup@v4 + - uses: pnpm/action-setup@v6 # Intentionally omit registry-url: it injects a token-based .npmrc that # conflicts with npm's OIDC provenance flow. Trusted publishing handles auth. - - uses: actions/setup-node@v4 + - uses: actions/setup-node@v7 with: node-version: '24' - - uses: actions/download-artifact@v4 + - uses: actions/download-artifact@v8 with: path: artifacts/ @@ -213,18 +218,18 @@ jobs: publish-wrapper: name: Publish @simlin/serve needs: [validate, publish-platform] - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 if: startsWith(github.ref, 'refs/tags/serve-v') permissions: contents: read id-token: write steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v7 - - uses: pnpm/action-setup@v4 + - uses: pnpm/action-setup@v6 # Intentionally omit registry-url: trusted publishing handles auth (see publish-platform) - - uses: actions/setup-node@v4 + - uses: actions/setup-node@v7 with: node-version: '24' diff --git a/.github/workflows/ts-release.yml b/.github/workflows/ts-release.yml index 468aab263..c44bf38cb 100644 --- a/.github/workflows/ts-release.yml +++ b/.github/workflows/ts-release.yml @@ -12,11 +12,11 @@ permissions: jobs: validate: name: Validate version - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 outputs: version: ${{ steps.version.outputs.version }} steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v7 - name: Extract and validate version id: version @@ -46,9 +46,9 @@ jobs: build: name: Build and test needs: validate - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v7 - name: Install Rust toolchain run: | @@ -62,9 +62,9 @@ jobs: - name: Install wasm-opt run: ./scripts/install-binaryen.sh - - uses: pnpm/action-setup@v4 + - uses: pnpm/action-setup@v6 - - uses: actions/setup-node@v4 + - uses: actions/setup-node@v7 with: node-version: '24' cache: 'pnpm' @@ -86,7 +86,7 @@ jobs: (cd src/diagram && pnpm pack --pack-destination ../../tarballs) - name: Upload tarballs - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v7 with: name: npm-tarballs path: tarballs/*.tgz @@ -96,19 +96,19 @@ jobs: publish-engine: name: Publish @simlin/engine needs: [validate, build] - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 if: startsWith(github.ref, 'refs/tags/ts-v') permissions: contents: read id-token: write steps: - - uses: actions/download-artifact@v4 + - uses: actions/download-artifact@v8 with: name: npm-tarballs path: tarballs/ # Intentionally omit registry-url: trusted publishing handles auth (OIDC) - - uses: actions/setup-node@v4 + - uses: actions/setup-node@v7 with: node-version: '24' @@ -127,18 +127,18 @@ jobs: publish-core: name: Publish @simlin/core needs: [validate, build, publish-engine] - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 if: startsWith(github.ref, 'refs/tags/ts-v') permissions: contents: read id-token: write steps: - - uses: actions/download-artifact@v4 + - uses: actions/download-artifact@v8 with: name: npm-tarballs path: tarballs/ - - uses: actions/setup-node@v4 + - uses: actions/setup-node@v7 with: node-version: '24' @@ -156,18 +156,18 @@ jobs: publish-diagram: name: Publish @simlin/diagram needs: [validate, build, publish-core] - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 if: startsWith(github.ref, 'refs/tags/ts-v') permissions: contents: read id-token: write steps: - - uses: actions/download-artifact@v4 + - uses: actions/download-artifact@v8 with: name: npm-tarballs path: tarballs/ - - uses: actions/setup-node@v4 + - uses: actions/setup-node@v7 with: node-version: '24' diff --git a/.github/workflows/wasm-opt.yml b/.github/workflows/wasm-opt.yml index f44e940fa..3a7622f5c 100644 --- a/.github/workflows/wasm-opt.yml +++ b/.github/workflows/wasm-opt.yml @@ -88,7 +88,7 @@ env: jobs: wasm-opt: name: Build with wasm-opt and run the engine tests against it - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 # wasm-opt -O3 is ~225s on our two blobs (120s + 105s on a fast desktop), # on top of the ~120s the two wasm-release cargo builds take in parallel # (each is single-threaded under LTO and codegen-units=1, so they overlap @@ -96,7 +96,7 @@ jobs: timeout-minutes: 30 steps: - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v7 - name: Install Rust toolchain run: rustup show @@ -109,16 +109,16 @@ jobs: run: ./scripts/install-binaryen.sh - name: Install pnpm - uses: pnpm/action-setup@v4 + uses: pnpm/action-setup@v6 - name: Install node - uses: actions/setup-node@v4 + uses: actions/setup-node@v7 with: node-version: '22' cache: 'pnpm' - name: Cache cargo registry and target - uses: actions/cache@v4 + uses: actions/cache@v6 with: path: | ~/.cargo/registry/index/ diff --git a/CLAUDE.md b/CLAUDE.md index 349ed1392..4b90ac0e4 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -24,7 +24,6 @@ This is a monorepo without external users -- breaking changes are OK if tests pa | `src/app` | TypeScript | Full-featured SD application | [CLAUDE.md](/src/app/CLAUDE.md) | | `src/notebook-widget` | TypeScript | anywidget module hosting the Editor in notebook cells (pysimlin) | [CLAUDE.md](/src/notebook-widget/CLAUDE.md) | | `src/server` | TypeScript | Express.js backend (Firebase Auth, Firestore) | [CLAUDE.md](/src/server/CLAUDE.md) | -| `src/xmutil` | C++/Rust | Vensim-to-XMILE converter (test-only) | -- | | `src/simlin-cli` | Rust | CLI for simulation/conversion (testing/debugging) | [CLAUDE.md](/src/simlin-cli/CLAUDE.md) | | `src/pysimlin` | Python/Rust | Python bindings for the simulation engine | [CLAUDE.md](/src/pysimlin/CLAUDE.md) | | `website` | TypeScript | Rspress-based documentation site | [CLAUDE.md](/website/CLAUDE.md) | diff --git a/Cargo.lock b/Cargo.lock index 121500769..dc0c1797a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1312,12 +1312,6 @@ dependencies = [ "rand_core 0.10.1", ] -[[package]] -name = "glob" -version = "0.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4eba85ea1d0a966a983acd07deee566e67395d2d96b6fb39e62b5a833f1eb0b" - [[package]] name = "globset" version = "0.4.19" @@ -3333,11 +3327,9 @@ dependencies = [ "simlin-engine", "smallvec", "tempfile", - "test-generator", "unicode-xid", "wasm-encoder", "wasm-interpreter", - "xmutil", ] [[package]] @@ -3605,17 +3597,6 @@ dependencies = [ "windows-sys 0.61.2", ] -[[package]] -name = "test-generator" -version = "0.3.0" -source = "git+https://github.com/bpowers/test-generator?rev=b78145bfb6a6f81425dfd6fbacb9c03624e79b2c#b78145bfb6a6f81425dfd6fbacb9c03624e79b2c" -dependencies = [ - "glob", - "proc-macro2", - "quote", - "syn 1.0.109", -] - [[package]] name = "thin-vec" version = "0.2.19" @@ -4392,13 +4373,6 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec7a2a501ed189703dba8b08142f057e887dfc4b2cc4db2d343ac6376ba3e0b9" -[[package]] -name = "xmutil" -version = "1.1.0" -dependencies = [ - "cc", -] - [[package]] name = "xxhash-rust" version = "0.8.18" diff --git a/Cargo.toml b/Cargo.toml index 6e437cad0..083faea1d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,7 +8,6 @@ members = [ "src/simlin-mcp", "src/simlin-mcp-core", "src/simlin-serve", - "src/xmutil", ] # Release builds optimize for speed (opt-level = 3) on every target. The wasm32 @@ -70,6 +69,23 @@ codegen-units = 1 inherits = "release" lto = false +# `gates` is what the `#[ignore]`d tests run under: scripts/gates.sh, by hand +# and in CI's `gates` job (.github/workflows/ci.yaml). +# +# They are ignored because a debug build takes minutes over the model corpus, +# C-LEARN and World3, so they need optimized code -- but not the shipping +# artifact. Dropping LTO makes the build parallel (each `release` test binary +# is its own single-threaded fat-LTO link), and the checks a debug build +# carries stay on, because these are the only tests that put the largest +# models through the engine and an overflow or a failed `debug_assert!` there +# is exactly what they should catch. +[profile.gates] +inherits = "release" +lto = false +strip = false +debug-assertions = true +overflow-checks = true + [profile.bench] debug = true strip = false diff --git a/deny.toml b/deny.toml index 310783d79..f97790c07 100644 --- a/deny.toml +++ b/deny.toml @@ -99,7 +99,6 @@ skip = [ unknown-registry = "deny" unknown-git = "deny" allow-git = [ - "https://github.com/bpowers/test-generator", # DLR-FT/wasm-interpreter backs the engine's WebAssembly simulation # backend (#620). Used unreleased and pinned by `rev`; there is no # crates.io release yet, so it is referenced directly from git. diff --git a/docs/README.md b/docs/README.md index ba44319ff..7d19fd6c2 100644 --- a/docs/README.md +++ b/docs/README.md @@ -71,7 +71,7 @@ ## Domain Knowledge - [reference/xmile-v1.0.html](reference/xmile-v1.0.html) -- XMILE interchange format specification -- [reference/vensim-macros.md](reference/vensim-macros.md) -- Vensim macros (`:MACRO:`): definition/call syntax, semantics (per-invocation stock state, locality, recursion), XMILE `` representation, xmutil's mapping, and implementation implications +- [reference/vensim-macros.md](reference/vensim-macros.md) -- Vensim macros (`:MACRO:`): definition/call syntax, semantics (per-invocation stock state, locality, recursion), XMILE `` representation, the mapping xmutil (the converter that produced the `.xmile` fixtures) gives them, and implementation implications - [reference/ltm--loops-that-matter.md](reference/ltm--loops-that-matter.md) -- Loops That Matter technique: link scores, loop scores, algorithm reference - [array-design.md](array-design.md) -- Array/subscript design notes diff --git a/docs/architecture.md b/docs/architecture.md index cf5b83ce4..2f819e1e5 100644 --- a/docs/architecture.md +++ b/docs/architecture.md @@ -16,7 +16,7 @@ Core simulation engine. Compiles, type-checks, unit-checks, and simulates SD mod - An alternative WebAssembly code-generation backend (`wasmgen/`) lowers a compiled model to one self-contained wasm module (no host imports) for fast repeated re-simulation; the VM stays the correctness oracle (every emitted module is checked against it). Surfaced through libsimlin `simlin_model_compile_to_wasm` - `builtins.rs` defines builtin functions (including `PREVIOUS`, `INIT`); stateful module functions (TREND, SMOOTH3) are model definitions in `stdlib/*.stmx`, generated into `stdlib.gen.rs` - Loops That Matter (`db/ltm/`, `db/ltm_ir.rs`, `db/analysis.rs`, `ltm_agg.rs`, `ltm_augment.rs`, `ltm_finding.rs`) compiles its synthetic variables through the same fragment pipeline as user variables; see [docs/design/ltm--loops-that-matter.md](/docs/design/ltm--loops-that-matter.md) -- Native Vensim MDL parser in `mdl/` (replaces C++ xmutil); see [docs/design/mdl-parser.md](/docs/design/mdl-parser.md) +- Native Vensim MDL parser in `mdl/`; see [docs/design/mdl-parser.md](/docs/design/mdl-parser.md) ### `src/libsimlin` (Rust) Flat "C" FFI wrapper around simlin-engine. Used from TypeScript (WASM), Go (CGo), and C/C++ (`simlin.h`). @@ -40,9 +40,6 @@ anywidget front-end module for pysimlin's `ModelWidget`: hosts `@simlin/diagram` ### `src/server` (TypeScript) Express.js backend. Firebase Auth (`authn.ts`), Firestore persistence (`models/db-firestore.ts`) in protobuf form. -### `src/xmutil` (C++ and Rust) -Rust wrapper around Bob Eberlein's xmutil C++ tool for converting Vensim models to XMILE. Only used for testing -- `src/simlin-engine/src/mdl` now fully implements this in Rust. - ### `src/simlin-mcp-core` (Rust) Transport-agnostic core library shared by every Simlin MCP server. Owns the `ProjectAccess` trait (the storage abstraction), the three reused tools (`read_model`, `edit_model`, `create_model`) as async free functions, the rmcp `ServerHandler` impl as `SimlinMcpServer`, and the wire-format types (`ErrorOutput`, `LoopDominanceSummary`, `DominantPeriodOutput`). @@ -68,10 +65,7 @@ The allowed dependency graph is enforced by `scripts/check-deps.py` reading from ### Rust ``` -xmutil (standalone) - ^ - | (optional, feature-gated) -simlin-engine +simlin-engine (leaf) ^ ^ ^ | | | simlin simlin-mcp-core @@ -81,13 +75,12 @@ simlin-cli simlin-mcp simlin-serve (also depends on simlin-engine directly) ``` -- `simlin-engine` -> `xmutil` (optional, feature-gated via `dep:xmutil`) +- `simlin-engine` -> (none) - `simlin` (libsimlin) -> `simlin-engine` - `simlin-mcp-core` -> `simlin-engine` - `simlin-mcp` -> `simlin-mcp-core`, `simlin-engine` - `simlin-serve` -> `simlin-mcp-core`, `simlin-engine` - `simlin-cli` -> `simlin-engine`, `simlin` -- `xmutil` -> (none) ### TypeScript diff --git a/docs/design/engine-performance.md b/docs/design/engine-performance.md index 415db145a..7c80c87b7 100644 --- a/docs/design/engine-performance.md +++ b/docs/design/engine-performance.md @@ -841,7 +841,7 @@ unreferenced constant leaves 5.4 MiB (plain) or 116.9 MiB (LTM) waiting. LTM), and libsimlin's database lock (`DbLock`) runs it when it drops, at the end of every entry point, so the drop is paid inside the edit that caused it rather than one edit late. The release is an exclusive salsa access -(`trigger_lru_eviction` -> `zalsa_mut` -> `cancel_others`, salsa 0.28.1 +(`trigger_lru_eviction` -> `zalsa_mut` -> `cancel_others`, salsa's `storage.rs`), which salsa counts in a `u8` and answers with a synthetic revision on the 256th without an input write (`runtime.rs`). `Durability::LOW`'s last-changed revision is the current revision itself, so after that bump diff --git a/docs/design/mdl-parser.md b/docs/design/mdl-parser.md index 26dee9343..5e400c921 100644 --- a/docs/design/mdl-parser.md +++ b/docs/design/mdl-parser.md @@ -6,9 +6,11 @@ For current status and agent guidance, see `src/simlin-engine/src/mdl/CLAUDE.md` ## Motivation -**Problems being solved:** -1. **Build complexity**: The C++ xmutil requires Bison/Flex, a C++ toolchain, and complex cross-compilation setup -2. **WASM compatibility**: Cannot easily include xmutil in WASM builds today; would require a large WASI build dependency +The reader is native Rust for two reasons: +1. **Build complexity**: it needs no parser generator, no C++ toolchain, and no cross-compilation setup beyond the engine's own. +2. **WASM compatibility**: it builds for wasm with the rest of the engine, so the browser imports `.mdl` files directly. + +Its algorithms are ported from xmutil, Bob Eberlein's C++ Vensim-to-XMILE converter (https://github.com/bobeberlein/xmutil). Where a comment cites an xmutil function or a `File.cpp:line`, it names the code a rule was ported from, under that repository's `src/` directory. What a model computes is settled by Vensim's own output (see "What Holds the Reader to Ground Truth"), not by xmutil. ## Architecture @@ -43,7 +45,7 @@ Some Vensim concepts require intermediate representations before conversion to d ## Vensim MDL Format Features -All features implemented in xmutil must be supported. This section documents the full feature set organized by implementation phase. +The feature set is xmutil's. This section documents it, organized by implementation phase. ### Phase 1: Lexer (`lexer.rs`) @@ -176,23 +178,17 @@ The production paths return errors rather than panicking: 4. Normalizer invariant (`normalizer.rs`): returns `Ok(None)` instead of panicking 5. Invariant unwraps in conversion/view processing: all use `match`/`continue`/`Option` -## C-LEARN Equivalence Analysis +## What Holds the Reader to Ground Truth -The C-LEARN model (`test/xmutil_test_models/C-LEARN v77 for Vensim.mdl`) exercises subscripts, subranges, bang notation, and element-specific equations extensively, and the equivalence harness compares the native import of it with xmutil's: +Vensim's own output does. The MDL corpus tests in `tests/integration/simulate.rs` (`simulate_mdl_path` and its variants) import a corpus `.mdl` that has a Vensim run beside it (`output.csv`, `output.tab`, or the model's `.dat`), simulate it, and compare the result with that run. A change to how a file imports is right when every such test still passes. -```bash -cargo test --release -p simlin-engine --features xmutil --test integration test_clearn_equivalence -- --ignored --nocapture -``` +The census in `tests/integration/mdl_vensim_truth.rs` asks the same question of every corpus `.mdl` with a Vensim run beside it, and pins, for each file that differs, the series that differ (`mdl_vensim_truth_series.tsv`). It is a gate: `scripts/gates.sh mdl_vensim_truth`. -The differences fall into these classes: +The gates that read a `.mdl` and compare Simlin with Simlin (the save fixed point, the save-meaning check) cannot stand in for them: a reader that is wrong is wrong on both sides of those. -- `:NA:` is the finite `float::NA` (`-2^109`), where xmutil writes `nan`. -- A lookup-only table's equation is empty, where xmutil writes the `0+0` sentinel; the engine reads both as a table-only variable. -- A variable's own name in its equation, where xmutil writes `self`; `time` in a macro, where xmutil writes `time$`. -- A dimension's element-level mapping onto `cop`, which xmutil leaves empty. -- A graphical function's y scale (two tables), unexamined. +C-LEARN (`test/xmutil_test_models/C-LEARN v77 for Vensim.mdl`) exercises subscripts, subranges, bang notation, and element-specific equations extensively. It is held to its own Vensim reference run by `simulates_clearn` and its companions in `tests/integration/simulate.rs`. -### Key C++ Reference Code for Subscript Handling +### Key xmutil Reference Code for Subscript Handling - `ContextInfo.cpp:7-60` (`GetLHSSpecific`): Per-element dimension reference substitution - `SymbolList.cpp:29-50` (`SetOwner`): Ownership assignment for subrange detection diff --git a/docs/dev/commands.md b/docs/dev/commands.md index bf860a55f..57c691a19 100644 --- a/docs/dev/commands.md +++ b/docs/dev/commands.md @@ -29,11 +29,12 @@ Run at the start of every session: | Command | Description | |---------|-------------| -| `cargo test` | Run all Rust tests | +| `cargo test` | Run all Rust tests but the gates | +| `scripts/gates.sh` | Run the gates: the `#[ignore]`d tests, and the rest of the engine suite with the `ext_data` feature on, optimized (`scripts/gates.sh ` for some of them) | | `pnpm test` | Run all TypeScript tests | | `pnpm tsc` | TypeScript type checking | -`cargo test --workspace` runs under a 3-minute wall-clock cap in both the pre-commit hook (`timeout(1)`) and CI (GitHub Actions `timeout-minutes: 3`). See [rust.md](rust.md#test-time-budgets) for per-test budget guidance and how to handle legitimately expensive tests. +`cargo test --workspace` runs under a 3-minute wall-clock cap in both the pre-commit hook (`timeout(1)`) and CI (GitHub Actions `timeout-minutes: 3`). A test too heavy for that suite is a gate: `#[ignore = "; run under the gates profile"]`, run by `scripts/gates.sh` and by CI's `gates` job on every push to main and every pull request to main. See [rust.md](rust.md#test-time-budgets) for the budget and for what makes a test a gate. ## Code Coverage @@ -71,16 +72,6 @@ cargo test -p simlin-engine # Engine tests only cargo test -p simlin-engine mdl:: # MDL parser tests ``` -### simlin-engine MDL equivalence tests - -```bash -# Run MDL equivalence tests (requires xmutil feature) -cargo test -p simlin-engine --features xmutil test_mdl_equivalence -- --nocapture - -# Run C-LEARN equivalence test (large model, ignored by default) -cargo test -p simlin-engine --features xmutil test_clearn_equivalence -- --ignored --nocapture -``` - ### pysimlin (Python) ```bash diff --git a/docs/dev/rust.md b/docs/dev/rust.md index f0db34b07..97fc3b997 100644 --- a/docs/dev/rust.md +++ b/docs/dev/rust.md @@ -27,20 +27,35 @@ Conventions inside a harness: ### Test time budgets -Individual tests should finish in a few seconds on a debug build. Target is under 2s per test; 5s is the soft ceiling. Slow tests compound: we have thousands of them and they run on every pre-commit and every CI push. - -`cargo test --workspace` is wrapped in a 3-minute wall-clock cap in both `scripts/pre-commit` (via `timeout(1)` from GNU coreutils) and `.github/workflows/ci.yaml` (via the step-level `timeout-minutes` field). CI baseline is ~60s, so the cap is ~3x headroom; a run that trips it means something has regressed and the build will fail. If the whole suite legitimately grows past the cap, raise both call sites in the same commit -- do not bypass the hook with `--no-verify`. +`cargo test --workspace` is wrapped in a 3-minute wall-clock cap in both `scripts/pre-commit` (via `timeout(1)` from GNU coreutils) and `.github/workflows/ci.yaml` (via the step-level `timeout-minutes` field). A run that trips it fails the build. If the whole suite legitimately grows past the cap, raise both call sites in the same commit -- do not bypass the hook with `--no-verify`. Pre-commit needs `timeout(1)` on PATH. Linux distros ship it as `timeout`; on macOS install via `brew install coreutils` (the binary is named `gtimeout` there, and the pre-commit hook picks up whichever is present). -To find slow tests, grep the per-binary durations from a regular run: +**The budget is CPU-seconds, not seconds.** The cap is wall-clock on a CI runner with four cores, and cargo runs the test binaries one after another, so every CPU-second a test spends on any thread is a quarter of a second of that runner's wall clock, or more on a slow one. A test that fans out over rayon finishes in a second on a 32-core desktop and still costs what it costs: libtest's per-test time and a desktop's wall clock both hide it. The suite has little room left (the capped step takes 56 to 127 s across CI runners for one and the same commit, and the slow end is the one that matters), so: + +- A test in the default suite is small: well under one CPU-second on a debug build. Measure CPU, not wall, with the test alone in its process: + + ```bash + # from the crate directory (src/simlin-engine), where fixture paths resolve + /usr/bin/time -f '%e s wall, %U s cpu' ../../target/debug/deps/ --exact :: + ``` + +- Anything heavier is a gate (below): a sweep of the model corpus, C-LEARN or World3 compiled or run, a large proptest. Parallelism inside the test does not make it cheap. +- Compare the whole suite before and after a change by its CPU on four cores, which is what a runner has: + + ```bash + cargo test --workspace --no-run + RUST_TEST_THREADS=4 taskset -c 0-3 /usr/bin/time -v cargo test --workspace # read "User time" + ``` + + CI's own wall clock is no measure of a change: runner to runner it varies by more than a factor of two. + +To find what is slow, grep the per-binary durations from a regular run: ```bash cargo test --workspace 2>&1 | grep 'finished in' ``` -Anything over a few seconds is worth looking at. - For PER-TEST durations, run the compiled test binary directly with libtest's (nightly-gated, but stable-toolchain-accessible) report-time flag: @@ -52,32 +67,37 @@ RUSTC_BOOTSTRAP=1 ../../target/debug/deps/ -Z unstable-options --report- 2>&1 | grep 'ok <' | sort -t'<' -k2 -rn | head -20 ``` -A binary's parallel wall clock is `max(longest single test, total/threads)`, so -one serial mega-test sets the floor no matter how many cores are available. -Prefer one `#[test]` per fixture (or a rayon `par_iter` inside a corpus test) -over a single test that loops a fixture list serially. +That is wall time under contention, so it ranks single-threaded tests and says nothing about one that fans out; use the CPU measurement above for those. A binary's parallel wall clock is `max(longest single test, total CPU/threads)`, so one serial mega-test sets the floor no matter how many cores are available: prefer one `#[test]` per fixture over a single test that loops a fixture list serially. -#### Testing threshold gates without building giant fixtures - -If you have a production gate like `MAX_FOO = 10_000`, do NOT test it by constructing a fixture with 10,001 items -- that ties test runtime to the production constant and makes every test run pay the full gate cost. PR #461 was reverted for exactly this: a test built 10,001 disjoint 3-cycles (~30k variables) so that `model_ltm_variables` would trip `MAX_LTM_TOTAL_CIRCUITS`, and the binary took 44 minutes. +#### Gates: the tests the default suite cannot run -Instead: - -- Expose a test-only constant (e.g. a `#[cfg(test)] const` or a field threaded through the API) that the test can set to a tiny value (5, 10) and trip with a correspondingly tiny fixture. -- Or pick a gate whose shape is cheap to exercise (e.g. the `MAX_LTM_SCC_NODES = 50` structural gate at the checkpoint needed a 51-node SCC to trip -- that's 51 variables, not 30,000). - -If a test MUST do expensive work (full compilation of a real-world model, enumeration over a large graph for a correctness claim), gate it with `#[ignore]` and document the opt-in command next to the test, for example: +A gate is a test that is `#[ignore]`d because a debug build takes too long over it. `cargo test` and the pre-commit hook skip it; `scripts/gates.sh` runs it, under the `gates` cargo profile (optimized, with debug assertions and overflow checks), and CI's `gates` job runs that script on every push to main and every pull request to main. An ignored test nothing runs holds nothing -- a pin a later change moves is found by whoever next runs it by hand -- so the job is what makes an ignored test a check. ```rust -// Run with: cargo test --release -- --ignored my_expensive_test +/// Every corpus model's save keeps what the model simulates. #[test] -#[ignore] -fn my_expensive_test() { ... } +#[ignore = "every corpus model saved, read back and simulated; run under the gates profile"] +fn every_corpus_save_keeps_what_the_model_simulates() { ... } +``` + +```bash +scripts/gates.sh # every gate +scripts/gates.sh every_corpus_save # the tests a filter selects, ignored or not +scripts/gates.sh --nocapture clearn # libtest flags pass through ``` -Prefer `--release` for expensive tests -- enumeration, simulation, and layout code can be 10-50x faster than debug. +The rules: -**`#[ignore]` for runtime is a judgement about today's engine, so re-take it after the engine gets faster.** An ignored gate does not run in pre-commit or CI, which means it catches nothing until someone remembers to ask for it -- `clearn_ltm_var_count_guardrail`'s own doc comment recorded a regression that slipped through for exactly that reason. After a compile-time improvement, time the ignored set and un-ignore what now fits: +- **`#[ignore]` means "needs an optimized build", and nothing else.** The reason string says what the test sweeps and ends `; run under the gates profile`. Say what makes it heavy in a way that stays true ("C-LEARN under the wasm interpreter"), not how long it took one day. +- **Every ignored test passes in the gates job.** A test that records a known defect is not ignored: it asserts the defect (or lists it in an allowlist that fails when a listed entry stops reproducing) and runs in the default suite, so it fails when the defect is fixed. An ignored test that is expected to fail is a test nobody reads. +- **A gate fails when its property breaks.** Check that the same way as any other test: make the obvious wrong change and see it fail under `scripts/gates.sh`. +- **An instrument** -- an ignored test that prints a measurement or writes files for a person, and asserts nothing about them -- runs in the job too, so it has to run to completion; its reason string says it is one. +- Where it is cheap, keep a small default-suite test beside a gate that exercises the same code on one or two models, so the hook sees a break before CI does. +- `scripts/gates.sh` also runs the rest of each suite it covers, with the `ext_data` feature on: the only run of the suite with the Excel data provider compiled in. To give another crate gates, add it to `PACKAGES` in that script. + +The simlin-serve smoke test is ignored for a different reason (it spawns the built binary with its embedded SPA) and has a CI job of its own, `serve-smoke`. + +**`#[ignore]` for runtime is a judgement about today's engine, so re-take it after the engine gets faster.** Time the ignored set on a debug build and un-ignore what now fits the default suite's budget: ```bash cargo test -p simlin-engine --test integration --no-run @@ -85,13 +105,14 @@ RUSTC_BOOTSTRAP=1 cargo test -p simlin-engine --test integration -- --ignored \ -Z unstable-options --report-time 2>&1 | grep 'ok <' | sort -t'<' -k2 -rn ``` -Then check the whole suite against CI's budget rather than trusting a developer machine, which has far more cores than a runner: +#### Testing threshold gates without building giant fixtures + +If you have a production gate like `MAX_FOO = 10_000`, do NOT test it by constructing a fixture with 10,001 items -- that ties test runtime to the production constant and makes every test run pay the full gate cost. PR #461 was reverted for exactly this: a test built 10,001 disjoint 3-cycles (~30k variables) so that `model_ltm_variables` would trip `MAX_LTM_TOTAL_CIRCUITS`, and the binary took 44 minutes. -```bash -RUST_TEST_THREADS=4 taskset -c 0-3 cargo test --workspace # approximates a CI runner -``` +Instead: -When a test stays ignored, say WHY in the attribute or the doc comment, and make the reason falsifiable: "runtime class" goes stale, while "executing C-LEARN under the non-JIT wasm interpreter, which no compiler speedup touches" or "a strict subset of a test that now runs by default" does not. +- Expose a test-only constant (e.g. a `#[cfg(test)] const` or a field threaded through the API) that the test can set to a tiny value (5, 10) and trip with a correspondingly tiny fixture. +- Or pick a gate whose shape is cheap to exercise (e.g. the `MAX_LTM_SCC_NODES = 50` structural gate at the checkpoint needed a 51-node SCC to trip -- that's 51 variables, not 30,000). ## Code Quality diff --git a/docs/reference/vensim-macros.md b/docs/reference/vensim-macros.md index cedf09e3a..240d552da 100644 --- a/docs/reference/vensim-macros.md +++ b/docs/reference/vensim-macros.md @@ -19,8 +19,11 @@ Authoritative sources used (cited inline throughout): sections 2.2.1, 2.10, 3.6/3.6.1/3.6.2, and 4.8/4.8.1/4.8.2/4.8.3. - Local repository: the six `test/test-models/tests/macro_*` fixtures, the C-LEARN hero model, the engine MDL parser (`src/simlin-engine/src/mdl/`), the - XMILE serde layer (`src/simlin-engine/src/xmile/mod.rs`), and the bundled - `xmutil` Vensim-to-XMILE converter (`src/xmutil/third_party/xmutil/`). + XMILE serde layer (`src/simlin-engine/src/xmile/mod.rs`). +- `xmutil`, Bob Eberlein's C++ Vensim-to-XMILE converter + (https://github.com/bobeberlein/xmutil). File references of the form + `xmutil:Vensim/VYacc.y:99` are paths under its `src/` directory; the line + numbers are approximate, since that tree moves. A note on terminology: throughout, "the model" or "the whole-model" means the top-level model that *invokes* a macro, as distinguished from the contents of @@ -360,7 +363,7 @@ fixtures show: macro body, but at the model level functions-implemented-as-macros such as `SMOOTH`/`DELAY3` routinely receive full expressions as arguments). The MDL grammar both in `xmutil` and in the engine parses macro-call arguments as a - general `exprlist` (`src/xmutil/.../Vensim/VYacc.y:99`, + general `exprlist` (`xmutil:Vensim/VYacc.y:99`, `src/simlin-engine/src/mdl/parser.rs:528`), so *syntactically* any expression is accepted. @@ -429,7 +432,7 @@ form of the macro header and call. Specifically: - `xmutil`'s grammar rule for a macro header is `VPTT_macro VPTT_symbol '(' exprlist ')'` - (`src/xmutil/third_party/xmutil/Vensim/VYacc.y:99`), and `exprlist` only + (`xmutil:Vensim/VYacc.y:99`), and `exprlist` only permits `,` and `;` as separators (`VYacc.y:203-207`). There is no production that consumes a `:` between an input list and an output list. `xmutil` therefore supports only macros whose sole output is the macro-named variable. @@ -540,7 +543,7 @@ every internal variable; see section 7). - The macro's primary output is the value of the variable whose name equals the macro name. At a call site `lhs = MACRONAME(args)`, `lhs` takes that value. In `xmutil`'s XMILE output the macro's `` is literally just the macro - name (`src/xmutil/.../Xmile/XMILEGenerator.cpp:60-63`), and the macro's + name (`xmutil:Xmile/XMILEGenerator.cpp:60-63`), and the macro's `` block contains the equation that actually defines that macro-named variable -- the `` says "the output is the variable called ``," and `` says how that variable is computed. @@ -572,7 +575,7 @@ either the whole-model or other macros" (XMILE 1.0 section 3.6.1). `:MACRO:` it pushes a fresh `SymbolNameSpace` and on `:END OF MACRO:` it pops back to the model's namespace; the macro *name* alone is registered into the model's (main) namespace so it can be called -(`src/xmutil/third_party/xmutil/Vensim/VensimParse.cpp:679-694`). The only +(`xmutil:Vensim/VensimParse.cpp:679-694`). The only escape from the local namespace is the `$`-suffixed model-variable reference (section 3.1). @@ -838,22 +841,23 @@ datamodel/compiler. ## 7. xmutil's mapping (Vensim `:MACRO:` -> XMILE ``) -`xmutil` is the bundled C++ Vensim-to-XMILE converter -(`src/xmutil/third_party/xmutil/`). It is the tool that produced the `.xmile` -and `.stmx` files in the local fixtures, so its mapping is the de facto -"expected" Vensim->XMILE translation the engine's own MDL pipeline is measured -against. Reading its source, the mapping is: +`xmutil` is Bob Eberlein's C++ Vensim-to-XMILE converter +(https://github.com/bobeberlein/xmutil). It is the tool that produced the +`.xmile` and `.stmx` files in the local fixtures, so its mapping is the shape +those fixtures have, and the shape the engine's XMILE reader has to accept. +What a model computes is settled by Vensim's own output, not by `xmutil`. +Reading its source, the mapping is: ### 7.1 Parse-time (Vensim side) - The Vensim lexer recognizes `:MACRO:` and `:END OF MACRO:` as dedicated tokens `VPTT_macro` / `VPTT_end_of_macro` - (`src/xmutil/third_party/xmutil/Vensim/VensimLex.cpp:427-438`). + (`xmutil:Vensim/VensimLex.cpp:427-438`). - The grammar rule is `macrostart: VPTT_macro { vpyy_macro_start(); } VPTT_symbol '(' exprlist ')' { vpyy_macro_expression($3, $5); }` and `macroend: VPTT_end_of_macro { vpyy_macro_end(); }` - (`src/xmutil/third_party/xmutil/Vensim/VYacc.y:98-104`). Note again: the + (`xmutil:Vensim/VYacc.y:98-104`). Note again: the header parses `(' exprlist ')'` with **no `:` output-list production** -- so `xmutil` supports only the macro-name-is-the-output form. - `VensimParse::MacroStart()` pushes a *new local `SymbolNameSpace`* for the @@ -861,18 +865,18 @@ against. Reading its source, the mapping is: `MacroFunction` registered against the *main* (model) namespace -- so the macro name is callable from the model -- while its body variables live in the local namespace; `VensimParse::MacroEnd()` restores the model namespace - (`src/xmutil/third_party/xmutil/Vensim/VensimParse.cpp:679-694`). + (`xmutil:Vensim/VensimParse.cpp:679-694`). - A `MacroFunction` (subclass of `Function`) holds: the macro name, an `ExpressionList* mArgs` (the formal parameters), a private `SymbolNameSpace* pSymbolNameSpace` (the local namespace), and a vector of `EqUnitPair` (each body equation + its units) - (`src/xmutil/third_party/xmutil/Function/Function.h:106-135`). Body equations + (`xmutil:Function/Function.h:106-135`). Body equations encountered between `:MACRO:` and `:END OF MACRO:` are added to the active macro rather than to a model group -- `AddFullEq` checks `!mInMacro` before assigning a variable to a group - (`src/xmutil/third_party/xmutil/Vensim/VensimParse.cpp:181`). The completed + (`xmutil:Vensim/VensimParse.cpp:181`). The completed list of `MacroFunction`s is handed to the `Model` - (`src/xmutil/third_party/xmutil/Vensim/VensimParse.cpp:387`, + (`xmutil:Vensim/VensimParse.cpp:387`, `Model.h:58-62,127`). - Because a `MacroFunction` is a `Function`, a call to it inside an equation is parsed like any function call -- there is no special call-site syntax. @@ -881,7 +885,7 @@ against. Reading its source, the mapping is: The whole-model translation runs first, then "macros are presented as separate models." For each `MacroFunction` -(`src/xmutil/third_party/xmutil/Xmile/XMILEGenerator.cpp:55-78`): +(`xmutil:Xmile/XMILEGenerator.cpp:55-78`): 1. Create a `` element with `name=""` (original Vensim casing, spaces preserved -- e.g. `name="EXPRESSION MACRO"`). @@ -902,7 +906,7 @@ models." For each `MacroFunction` `` (`XMILEGenerator.cpp:77`). `MacroFunction::ComputableName()` returns `SpaceToUnderBar(name)` -(`src/xmutil/third_party/xmutil/Function/Function.cpp:21-23`), so when the macro +(`xmutil:Function/Function.cpp:21-23`), so when the macro is *called* in a model equation, `xmutil` emits the underscored name -- e.g. the Vensim call `EXPRESSION MACRO(macro input, macro parameter)` becomes the XMILE equation `EXPRESSION_MACRO(macro_input, macro_parameter)` (exactly what @@ -991,9 +995,9 @@ by its underscored name. ### 7.5 What the engine's *native* MDL parser does today -The engine's pure-Rust MDL parser (`src/simlin-engine/src/mdl/`) -- which is -replacing `xmutil` -- already *parses* macros into a `MacroDef { name, args: -Vec, equations: Vec, loc }` +The engine's pure-Rust MDL parser (`src/simlin-engine/src/mdl/`) *parses* +macros into a `MacroDef { name, args: Vec, equations: Vec, +loc }` (`src/simlin-engine/src/mdl/ast.rs:483-493`), assembled by the reader's `MacroState` machine (`src/simlin-engine/src/mdl/reader.rs:59-70,300-330`). But the AST-to-datamodel conversion **discards** it: `MdlItem::Macro(_)` is matched diff --git a/docs/tech-debt.md b/docs/tech-debt.md index 57c1de4bd..4fa260dd1 100644 --- a/docs/tech-debt.md +++ b/docs/tech-debt.md @@ -4,16 +4,6 @@ Known debt items consolidated from CLAUDE.md files and codebase analysis. Each e ## Items -### 1. MDL Parser C-LEARN Equivalence - -- **Component**: simlin-engine (src/simlin-engine/src/mdl/) -- **Severity**: medium -- **Description**: 26 differences remain between the native Rust MDL parser and the C++ xmutil reference path. Root causes: missing initial-value comments, trailing tabs in dimension names, net flow synthesis differences, middle-dot canonicalization, GF y-scale computation. -- **Measure**: `cargo test -p simlin-engine --features xmutil test_clearn_equivalence -- --ignored --nocapture 2>&1 | grep 'DIFF'` -- **Count**: 26 diffs (as of January 2026) -- **Owner**: unassigned -- **Last reviewed**: 2026-02-15 - ### 2. `unwrap_or_default()` Usage in simlin-engine - **Component**: simlin-engine @@ -103,16 +93,6 @@ Known debt items consolidated from CLAUDE.md files and codebase analysis. Each e - **Owner**: unassigned - **Last reviewed**: 2026-09-03 -### 13. Ignored Rust Tests - -- **Component**: simlin-engine -- **Severity**: low -- **Description**: 23 tests are marked `#[ignore]`. 11 are in tests/simulate.rs (tracked individually by GitHub issues: #346 DELAY FIXED ring-buffer [4 tests], #347 GET DATA BETWEEN TIMES+implicit .dat loading [2 tests], #348 directdata/directconst/directlookups/directsubs [4 tests], #349 C-LEARN macro expansion [1 test]). 8 are in vdf.rs (VDF binary format tests). 2 are in json_sdai_proptest.rs (file system writes). 1 is in tests/mdl_equivalence.rs (tracked by item 1). 1 is in tests/mdl_roundtrip.rs. The close-array-gaps work enabled all 8 array_tests.rs tests plus 5 simulate.rs tests (#345 EXCEPT tests and the basic EXCEPT test). -- **Measure**: `rg '#\[ignore\]' --type rust src/simlin-engine/ -c` -- **Count**: 23 ignored tests across 5 files (as of 2026-03-12) -- **Owner**: unassigned -- **Last reviewed**: 2026-03-12 - ### 14. TypeScript Test Coverage Gaps - **Component**: app, core, server, engine, diagram @@ -552,7 +532,7 @@ Known debt items consolidated from CLAUDE.md files and codebase analysis. Each e - **Component**: build / CI (`scripts/pre-commit`, `.github/workflows/`, `src/simlin-engine/Cargo.toml`) - **Severity**: low (was medium; the dev-target half of this is now structurally impossible — see below) -- **Description**: Neither the pre-commit hook nor CI exercises a Cargo **feature matrix** for `simlin-engine`, so feature-gating bugs in the **library** are invisible to the canonical gate. simlin-engine features: `file_io`, `schema`, `ai_info`, `debug-derive` (default = `schema, ai_info, debug-derive`), plus `xmutil`, `ext_data`, `png_render`, `ltm_bench`. +- **Description**: Neither the pre-commit hook nor CI exercises a Cargo **feature matrix** for `simlin-engine`, so feature-gating bugs in the **library** are invisible to the canonical gate. simlin-engine features: `file_io`, `schema`, `ai_info`, `debug-derive` (default = `schema, ai_info, debug-derive`), plus `ext_data`, `png_render`, `ltm_bench`. **Historical failure (no longer reachable).** During Phase 7c of the Vensim macro epic a subagent added `src/simlin-engine/tests/metasd_macros.rs` (which calls the `file_io`-gated `load_dat`/`load_csv`) WITHOUT the mirroring `required-features = ["file_io"]` entry. That broke `cargo build -p simlin-engine --all-targets` whenever `file_io` was *not* enabled (`error[E0425]`, `load_dat`/`load_csv` configured out), and survived multiple commits because the workspace build pulls `file_io` into scope via cross-crate feature unification (`simlin-cli` requests `simlin-engine/file_io`). The Cargo.toml fix landed in `7cba4ad2`. GH #925 then removed the underlying hazard: the crate takes a self dev-dependency (`simlin-engine = { path = ".", default-features = false, features = ["file_io"] }`), so `file_io` is unconditionally on for every test/bench/example target. A dev target can no longer break under a missing `file_io` — that entire failure class is gone, and the `#[cfg]`/`required-features` bookkeeping it demanded is gone with it. diff --git a/scripts/ci-report-failure.sh b/scripts/ci-report-failure.sh new file mode 100755 index 000000000..bec64a1b1 --- /dev/null +++ b/scripts/ci-report-failure.sh @@ -0,0 +1,40 @@ +#!/usr/bin/env bash +# Open (or add to) the GitHub issue that says a scheduled workflow failed. +# +# A push or a pull request has an author who is told when its checks fail. A +# scheduled run has nobody: its red mark sits on the Actions tab until someone +# happens to look. Workflows that run on a schedule call this from a step +# guarded by `if: failure() && github.event_name == 'schedule'`, so the failure +# lands where open work is tracked. One open issue per title: a failure that +# repeats nightly adds a comment to it rather than opening another. +# +# usage: ci-report-failure.sh [file with details] +# needs: GH_TOKEN with `issues: write`, and the GITHUB_* variables Actions sets. +set -euo pipefail + +title="$1" +details="${2:-}" + +run_url="${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}/actions/runs/${GITHUB_RUN_ID}" +body="$(mktemp)" +{ + echo "The scheduled \`${GITHUB_WORKFLOW}\` run failed at ${GITHUB_SHA}: ${run_url}" + if [ -n "$details" ] && [ -s "$details" ]; then + echo + echo '```' + # GitHub rejects a body over 65,536 characters; the tail is where a + # tool prints its verdict. + tail -c 20000 "$details" + echo '```' + fi +} > "$body" + +existing="$(gh issue list --repo "$GITHUB_REPOSITORY" --state open \ + --search "in:title \"$title\"" --json number,title \ + --jq "map(select(.title == \"$title\")) | .[0].number // empty")" + +if [ -n "$existing" ]; then + gh issue comment "$existing" --repo "$GITHUB_REPOSITORY" --body-file "$body" +else + gh issue create --repo "$GITHUB_REPOSITORY" --title "$title" --body-file "$body" +fi diff --git a/scripts/dep-policy.json b/scripts/dep-policy.json index 2256e970c..ddc1c8c35 100644 --- a/scripts/dep-policy.json +++ b/scripts/dep-policy.json @@ -1,12 +1,11 @@ { "rust": { - "simlin-engine": ["xmutil"], + "simlin-engine": [], "simlin": ["simlin-engine"], "simlin-cli": ["simlin-engine", "simlin"], "simlin-mcp": ["simlin-engine", "simlin-mcp-core"], "simlin-mcp-core": ["simlin-engine"], - "simlin-serve": ["simlin-engine", "simlin-mcp-core"], - "xmutil": [] + "simlin-serve": ["simlin-engine", "simlin-mcp-core"] }, "typescript": { "@simlin/engine": [], diff --git a/scripts/gates.sh b/scripts/gates.sh new file mode 100755 index 000000000..e0b1c5cd7 --- /dev/null +++ b/scripts/gates.sh @@ -0,0 +1,48 @@ +#!/usr/bin/env bash +# Run the gates: every test the default suite cannot, under an optimized build. +# +# A test is `#[ignore]`d when a debug build takes too long over it (a sweep of +# the model corpus, C-LEARN or World3 compiled and run), so `cargo test` and +# the pre-commit hook skip it and this is what runs it -- here, and in CI's +# `gates` job, which calls this script. The build is the `gates` cargo profile +# (workspace Cargo.toml): optimized, with debug assertions and overflow checks. +# +# It runs the ignored tests AND the rest of each suite. The rest costs little +# once optimized, and it is the only run of the suite with a cargo feature +# no default build turns on compiled in (FEATURES below). +# +# usage: +# scripts/gates.sh every gate +# scripts/gates.sh ... the tests a libtest filter selects, +# ignored or not (flags pass through: +# scripts/gates.sh --nocapture clearn) +# scripts/gates.sh --no-run build only (CI times the build apart +# from the run) +set -euo pipefail + +cd "$(git rev-parse --show-toplevel)" + +# The packages with gates. To add one, list it here with `-p`; its features go +# in FEATURES in the same `package/feature` form. +PACKAGES=(-p simlin-engine) + +# Features no other lane runs the suite with: `ext_data` (the Excel data +# provider). +FEATURES="simlin-engine/ext_data" + +# The unit tests and the one integration harness. Naming them keeps the +# doctests and the allocator-counting harness (tests/vm_alloc.rs), which hold +# no gate, from being built under a second profile. +TARGETS=(--lib --test integration) + +# Gates known to fail, skipped by exact name so every other gate still runs. +# The C-LEARN LTM digest's pin is stale; the change that re-pins it deletes +# this skip. +SKIP=(--skip simulate_ltm::clearn_ltm_slot_maxima_digest) + +if [ "${1:-}" = "--no-run" ]; then + exec cargo test "${PACKAGES[@]}" --profile gates --features "$FEATURES" "${TARGETS[@]}" --no-run +fi + +exec cargo test "${PACKAGES[@]}" --profile gates --features "$FEATURES" "${TARGETS[@]}" \ + --no-fail-fast -- --include-ignored "${SKIP[@]}" "$@" diff --git a/scripts/pre-commit b/scripts/pre-commit index dbf2f9758..5089efaac 100755 --- a/scripts/pre-commit +++ b/scripts/pre-commit @@ -142,13 +142,16 @@ trap cleanup_pipelines EXIT INT TERM cargo test --no-run # Wall-clock cap on `cargo test`. A healthy post-compile run is - # well under a minute; 180s is a ~3x safety rail that trips only - # when a single test or the whole suite regresses into the - # pathological territory that caused PR #461 (a single test binary - # took 44 minutes there, and the CI job hit GitHub's 60-minute - # per-job cap). SIGTERM first, SIGKILL 30s later so the in-flight - # test output flushes before we lose the process. See - # docs/dev/rust.md for per-test budget guidance. + # well under a minute on a developer machine; 180s is a safety rail + # that trips only when a single test or the whole suite regresses + # into the pathological territory that caused PR #461 (a single + # test binary took 44 minutes there, and the CI job hit GitHub's + # 60-minute per-job cap). The same cap in CI is far tighter: a + # runner has four cores, so the suite's budget is its CPU, and a + # run that is comfortable here can be at the cap there. SIGTERM + # first, SIGKILL 30s later so the in-flight test output flushes + # before we lose the process. See docs/dev/rust.md for the budget + # and for what belongs in the gates instead. echo "[rust] Running tests..." TEST_TMP="$(mktemp)" # The `&& TEST_EXIT=0 || TEST_EXIT=$?` pattern is load-bearing: diff --git a/src/engine/build.sh b/src/engine/build.sh index 66979decb..7a43e80d2 100755 --- a/src/engine/build.sh +++ b/src/engine/build.sh @@ -29,8 +29,6 @@ mkdir -p core # only, under LTO) on a clean build. Nesting them keeps both inside whatever # `cargo clean` and the CI caches cover. # -# The xmutil feature is always off here (C++ dependency, not wasm-buildable). -# # The workspace target directory is RESOLVED, not assumed: `CARGO_TARGET_DIR` # and a cargo config's `build.target-dir` both move it, and a hardcoded # `../../target` turns that into a `cp: cannot stat` below -- which reads as a diff --git a/src/libsimlin/src/lib.rs b/src/libsimlin/src/lib.rs index 8ca8ae432..44f916bea 100644 --- a/src/libsimlin/src/lib.rs +++ b/src/libsimlin/src/lib.rs @@ -544,7 +544,7 @@ impl Drop for Waiting<'_> { /// execution is what replaces a memo) needs an event callback registered on /// the storage, and with one registered salsa constructs a /// `WillCheckCancellation` event -- `thread::current().id()` included -- on -/// every memo fetch (salsa 0.28.1 `zalsa.rs` `unwind_if_revision_cancelled`, +/// every memo fetch (salsa's `zalsa.rs` `unwind_if_revision_cancelled`, /// `function/fetch.rs`, `event.rs` `Event::new`): a cost on every hit to save /// a bounded walk. pub struct DbLock<'a>(MutexGuard<'a, engine::db::SimlinDb>); diff --git a/src/simlin-engine/CLAUDE.md b/src/simlin-engine/CLAUDE.md index f9ba20a90..900a65d68 100644 --- a/src/simlin-engine/CLAUDE.md +++ b/src/simlin-engine/CLAUDE.md @@ -81,6 +81,7 @@ Equation text becomes a running simulation in six stages. Each stage names its o - `db/var_fragment.rs`, `db/fragment_compile.rs`, `db/layout.rs`, `db/assemble.rs` -- the fragment constructors and assembly (pipeline stages 4 and 5). - `db/analysis.rs`, `db/ltm_ir.rs`, `db/ltm/` -- LTM (below). - `db/exec_probe.rs` -- `ProbedDb`, the execution-count probe the incrementality tests read. +- Interned keys are collected and their slots reused: `LtmLinkId`, `ModuleInputSet` and the argument tuples salsa interns for a multi-argument query keep salsa's default `revisions`, so a key no revision has used for three is reclaimed for another. Salsa panics, which is an abort in a release build, if a reclaimable key's fields or memos are read without the key having been interned or validated in the current revision; no memoized value holds an interned id (every query result is `'static`), which is what keeps that from happening, and `db::interned_reuse_tests` drives edit histories that reuse slots (counting salsa's own reuse events) to hold it. ### Rules the salsa layer depends on @@ -235,17 +236,19 @@ Opt-in: a model that declares units on no variable gets no unit diagnostics. `un - `edit.rs` is `edit_model`: operations applied in order to a copy of the project (`Builder`), refused when anything they write -- a variable, the sim specs, a loop name -- changed since the session's last `read_model` (provenance aside, `changes::same_record`; unless one of the session's own plans left it so: `Expected`, `AsRead`), placed by the incremental layout (an element `EditView` when it implies no model operation, else the whole view), and gated on the staged db (`gate`, staged under `runs::Staging` and stopping between its stages when the workspace says other work waits; `Gate::refusal`: an error the model did not have, by code and variable mapped through renames; specs whose run costs more than a run may, `runs::over_budget` against the model's own; a model that simulated and no longer does; a value that becomes non-finite; a unit-clean model's first unit warning; every new warning listed). In a project that records provenance, what the edit adds or edits is marked (`after_ai_edit`, ISEE's AI information). A plan that passes is kept under an id with its operations, engine patch, lines and what it writes before and after (`Plan`, `Written`); `land_plan` (`Session::land_plan`) lands it by construction: its patch at its revision, else its operations planned again on the contents as they are, fresh against what the plan was made on (`AsPlanned`), gated again, and with the lines the person approved. The output's lines are fitted to the budget. - `verify.rs` is `verify_findings`: each citation of a finding checked against the session at the current revision (runs made before the model changed, diagrams aside, are not evidence), a failure saying what is true instead, a finding every citation of which holds given an id keyed by its claim. An `equation` citation compares parsed equations in a normal form (`normal`: spacing, case and name spelling aside); `value`, `ends_near` and `compares` judge a number against the actual one (`near`: 5% of the larger of the two, or, for values near zero, `VALUE_FLOOR`, the summary's precision, of the series' largest magnitude), and a cited time outside the run is refused; a table variable's equation is its table at its input (`LOOKUP(name, input)`); a series at rest has no peak; `readers` is the exact set of what the variable links to. A cited battery check answers from the session's check log while the revision is the one it ran at, and is run again (`battery::recheck`) after an edit; loop citations read the run's loop analysis (`loops::leadership`, `loops_through`, `polarity_of`, and `through_of` for an element). It stops after the citation during which other work began to wait, giving out no id. - `behavior.rs` is the one statement of a behavior mode (`classify`, and `classify_at` for a caller that knows the scale): at rest, linear, exponential, goal seeking, S-shaped, overshoot, rise and fall, fall and rise, oscillation (damped, sustained, growing), undefined, other. It reads a series against its saved times, so unevenly saved rows read as the curve they sample where they resolve it, and every threshold is a fraction of the series' own range or magnitude, so a mode is the same at any scale, offset, direction and unit of time. At rest is a range under one unit of the last digit a summary reports, or a series that never leaves zero by more than the rounding floating point leaves at the scale its caller reads it at (`RESIDUE_EPSILONS`, 64, units of `f64::EPSILON` of that scale; a stock's scale already counts the run's horizon, so its steps are not counted again); there is no fixed floor, since residue and a tiny model are the same numbers and only the caller knows which. After trimming a still start and a settled end (each reported past a twentieth of the run; a share of the run is past a threshold only by more than a millionth, `past_share`, so a tie that evenly saved rows make reads alike on every clock), turning points are reversals over 1% of the range (`Shape::turns`, what a summary lists), and the mode is named for the large movements: reversals over a tenth of the range, less a first or last one under a quarter of the movement beside it and no longer than a quarter of the run (the run starting up or being cut off). No such turn is one movement, read from where a start-up transient ends, an overshoot when it ends short of its furthest by its largest reversal, else named by where it runs against its chord (along it, below it, above it, below then above); one is an overshoot, a rise and fall or a fall and rise; more are an oscillation, its damping the last swing against the first (with one whole swing, the movement after it is a second swing where it ends at a turning point, else the run cut it off). `behavior_tests.rs` holds the classifier to series from World3, C-LEARN and two small models labeled as a modeler names them (`behavior_real_series.tsv` in the default suite, a wider set and the file's fidelity to the models in an ignored gate), agreeing except where `KNOWN_DISAGREEMENTS` lists what it says instead and why (a late inflection reads as exponential; reversals over a tenth of the range make an oscillation of a growth curve), a table that fails when a listed row starts agreeing, to curves generated for each mode across parameters, densities, scales and clocks (a sample in the default suite, the full sweep an ignored gate), to rows either side of each threshold, and to the invariances as property tests. -- Tests go through `Session::call` over `TestProject` or corpus models (`tools/test_support.rs`'s `Host` syncs a db the way hosts do); `tools/tests.rs` validates every tool's input and output against the schemas the catalog publishes, with rows from `ToolName::ALL`, and `tools/corpus_tests.rs` (ignored; `--release -- --ignored`) sends every corpus model through the tools and checks every answer against its schema and the budget. +- Tests go through `Session::call` over `TestProject` or corpus models (`tools/test_support.rs`'s `Host` syncs a db the way hosts do); `tools/tests.rs` validates every tool's input and output against the schemas the catalog publishes, with rows from `ToolName::ALL`, and `tools/corpus_tests.rs` (a gate; `scripts/gates.sh tools::corpus_tests`) sends every corpus model through the tools and checks every answer against its schema and the budget. ## Tests - Unit tests live beside their code, either inline or in a sibling `*_tests.rs` mounted with `#[cfg(test)] #[path]` so `use super::*` still resolves. Integration tests are one harness, `tests/integration/main.rs` (add a `mod`, not a new `tests/*.rs`); `tests/vm_alloc.rs` is separate only because it installs a global allocator. `test_common::TestProject` is the model builder, exported behind the `test-support` feature for sibling crates' tests. - There is one compiler, in tests too: a test that needs a variable's lowered `Expr2` reads `db::lowered_source_variable` (or `model_lowered_variables` for a model's worth), a test that needs its compiled form reads it through the production per-variable lowering (`TestProject::flow_exprs`, over `db::var_fragment::explicit_fragment_input` + `compiler::fragment::lower_fragment`), a test that needs a refusal's location reads `TestProject::error_diagnostics`, and a loop-discovery test drives `ltm_finding::discover_loops_with_graph` with the graph and context `analysis::analyze_model` builds. Never add a whole-model parse, lowering or compile path for tests to compare against: it is a second implementation that every compiler change then has to be made in twice, and it can only agree with the production one by construction where the two share code. - What pins unit semantics is the unit suites over production diagnostics, and what pins sharing is `Arc::ptr_eq` against the production memos plus `ProbedDb` body counts (`db::lowered_variable_tests`). A fixture is what production produces: build models through `TestProject`, `open_xmile` or `open_vensim` and read results through the production queries. -- The `test/` corpus at the repo root holds models with expected outputs from other SD tools; the heavy C-LEARN and World3 gates are `#[ignore]`d and run with `--release -- --ignored`. Observe the time budgets in `docs/dev/rust.md`; test a threshold with a test-only override, not a fixture large enough to trip it. +- The `test/` corpus at the repo root holds models with expected outputs from other SD tools. +- A test too heavy for the default suite is a gate: `#[ignore = "; run under the gates profile"]`. `#[ignore]` means exactly "needs an optimized build", so every ignored test passes under `scripts/gates.sh` (the `gates` cargo profile: optimized, with debug assertions and overflow checks), which CI's `gates` job runs on every push to main and every pull request to main; a test that records a known defect asserts the defect in the default suite instead. The corpus sweeps and the C-LEARN and World3 gates are gates. The default suite's budget is CPU-seconds on four cores, which rayon hides from a test's wall time (`docs/dev/rust.md`); test a threshold with a test-only override, not a fixture large enough to trip it. +- `scripts/gates.sh` also runs the rest of the suite, with the `ext_data` feature (the Excel data provider) on, which no default build compiles. ## Cargo features and generated files -Features: `debug-derive` (gates `derive(Debug)`; new types use `#[cfg_attr(feature = "debug-derive", derive(Debug))]`, and libsimlin builds the engine with `default-features = false`, so an unconditional derive on a type holding a gated one passes `cargo check -p simlin-engine` and fails the hook's wasm and pysimlin builds -- check with `cargo check -p simlin-engine --no-default-features`), `schema`, `ai_info`, `file_io` (CSV/filesystem providers; a production dependency kept out of wasm, always on for test targets via the self dev-dependency), `ext_data`, `png_render`, `xmutil` (the C++ converter, used only to compare against), `ltm_bench`, `test-support`, `layout_eval`. +Features: `debug-derive` (gates `derive(Debug)`; new types use `#[cfg_attr(feature = "debug-derive", derive(Debug))]`, and libsimlin builds the engine with `default-features = false`, so an unconditional derive on a type holding a gated one passes `cargo check -p simlin-engine` and fails the hook's wasm and pysimlin builds -- check with `cargo check -p simlin-engine --no-default-features`), `schema`, `ai_info`, `file_io` (CSV/filesystem providers; a production dependency kept out of wasm, always on for test targets via the self dev-dependency), `ext_data`, `png_render`, `ltm_bench`, `test-support`, `layout_eval`. Generated, do not edit: `src/project_io.gen.rs` from `project_io.proto` (`pnpm build:gen-protobufs`) and `src/stdlib.gen.rs` from the repo-root `stdlib/*.stmx` (`pnpm rebuild-stdlib`). Both carry a hash that a test checks. diff --git a/src/simlin-engine/Cargo.toml b/src/simlin-engine/Cargo.toml index 8d48993d2..7fe03154c 100644 --- a/src/simlin-engine/Cargo.toml +++ b/src/simlin-engine/Cargo.toml @@ -24,7 +24,6 @@ png_render = ["dep:resvg"] # leaves it out, since no browser code calls it (it is about 5% of that # bundle). agent_tools = [] -xmutil = ["dep:xmutil"] # Compiles in the `generate_loop_score_variables` loop-score tracing # instrumentation (per-loop cumulative equation bytes + RSS sampling), a # benchmarking/diagnostic aid only. Off by default so production builds carry @@ -66,7 +65,6 @@ quick-xml = { version = "0.41", features = [ "serialize", "overlapped-lists" ] } csv = { version = "1", optional = true } calamine = { version = "0.33", optional = true } resvg = { version = "0.47", optional = true, default-features = false, features = ["text"] } -xmutil = { version = "1", path = "../xmutil", optional = true } # 0.28.5 is the floor: earlier 0.28 releases can free an interned value's # storage while it is in use (RUSTSEC-2026-0308). @@ -135,7 +133,6 @@ jsonschema = { version = "0.26", default-features = false } # terminal summary, so trim to the bench-runner harness alone. criterion = { version = "0.5", default-features = false, features = ["cargo_bench_support"] } sha2 = "0.10" -test-generator = { version = "0.3.0", git = "https://github.com/bpowers/test-generator", rev = "b78145bfb6a6f81425dfd6fbacb9c03624e79b2c" } ed25519-dalek = "2" tempfile = "3" diff --git a/src/simlin-engine/src/compat.rs b/src/simlin-engine/src/compat.rs index 85758a943..b415a4949 100644 --- a/src/simlin-engine/src/compat.rs +++ b/src/simlin-engine/src/compat.rs @@ -3,7 +3,7 @@ // Version 2.0, that can be found in the LICENSE file. use std::io::BufRead; -#[cfg(any(feature = "file_io", feature = "xmutil"))] +#[cfg(feature = "file_io")] use std::io::BufReader; use crate::common::Result; @@ -47,24 +47,6 @@ pub fn to_systems(project: &Project) -> Result { systems::project_to_systems(project) } -#[cfg(feature = "xmutil")] -pub fn open_vensim_xmutil(contents: &str) -> Result { - use crate::common::{Error, ErrorCode, ErrorKind}; - use xmutil::convert_vensim_mdl; - - let (xmile_src, logs) = convert_vensim_mdl(contents, false); - if xmile_src.is_none() { - return Err(Error::new( - ErrorKind::Import, - ErrorCode::VensimConversion, - Some("xmutil error: ".to_owned() + logs.as_ref().unwrap_or(&"(no logs)".to_owned())), - )); - } - let xmile_src = xmile_src.unwrap(); - let mut f = BufReader::new(xmile_src.as_bytes()); - xmile::project_from_reader(&mut f) -} - /// Parse a Vensim MDL file using the native Rust parser. pub fn open_vensim(contents: &str) -> Result { open_vensim_with_data(contents, None) diff --git a/src/simlin-engine/src/datamodel.rs b/src/simlin-engine/src/datamodel.rs index 15deffec7..6af0d9d11 100644 --- a/src/simlin-engine/src/datamodel.rs +++ b/src/simlin-engine/src/datamodel.rs @@ -77,16 +77,9 @@ macro_rules! bitwise_eq { } #[cfg_attr(feature = "debug-derive", derive(Debug))] -#[derive(Default, Eq, Clone)] +#[derive(Default, PartialEq, Eq, Clone)] pub struct UnitMap { pub map: BTreeMap, - pub ctx: Option>, -} - -impl PartialEq for UnitMap { - fn eq(&self, other: &Self) -> bool { - self.map == other.map - } } impl UnitMap { @@ -127,14 +120,6 @@ impl UnitMap { self } - pub fn push_ctx(mut self, ctx: String) -> Self { - let mut full_ctx = self.ctx.take().unwrap_or_default(); - full_ctx.push(ctx); - self.ctx = Some(full_ctx); - - self - } - #[allow(dead_code)] pub fn pretty_print(&self) -> String { format!("{self}") @@ -154,7 +139,6 @@ impl std::ops::Mul for UnitMap { type Output = Self; fn mul(mut self, rhs: Self) -> Self::Output { - let mut rhs = rhs; for (unit, n) in rhs.map.into_iter() { let new_value = match self.map.get(&unit) { None => n, @@ -170,14 +154,6 @@ impl std::ops::Mul for UnitMap { } } - if let Some(rctx) = rhs.ctx.take() - && !rctx.is_empty() - { - let mut ctx = self.ctx.take().unwrap_or_default(); - ctx.extend(rctx); - self.ctx = Some(ctx); - } - self } } @@ -242,7 +218,6 @@ impl FromIterator<(String, i32)> for UnitMap { fn from_iter>(iter: I) -> Self { UnitMap { map: iter.into_iter().collect(), - ctx: None, } } } diff --git a/src/simlin-engine/src/db.rs b/src/simlin-engine/src/db.rs index d9c0330ac..12447ae9e 100644 --- a/src/simlin-engine/src/db.rs +++ b/src/simlin-engine/src/db.rs @@ -252,7 +252,7 @@ pub struct SimlinDb { /// re-syncs onto the PRIOR handles, so the set is created once and reused /// forever after. An ordinary model never allocates it at all. /// - /// The slot is deliberately never cleared. Salsa 0.26 has no input reclamation, + /// The slot is deliberately never cleared. Salsa has no input reclamation, /// so dropping these handles would free nothing -- the `SourceProject`/ /// `SourceModel`/`SourceVariable` inputs and their memos stay in the arena -- /// while forcing the next expanded sync down the `prev == None` path, which @@ -372,7 +372,7 @@ impl SimlinDb { /// Free the memos this revision's queries replaced. /// /// Salsa does not drop a superseded memo when a query re-executes: - /// `insert_memo` (salsa 0.28.1 `function.rs`) pushes the old one onto the + /// `insert_memo` (salsa's `function.rs`) pushes the old one onto the /// ingredient's deferred-delete list, which `reset_for_new_revision` /// clears at the START of the next revision, i.e. inside the next input /// write. A long-lived database that answers queries after a sync and @@ -1494,6 +1494,8 @@ mod implicit_module_tests; #[cfg(test)] mod incremental_compile_tests; #[cfg(test)] +mod interned_reuse_tests; +#[cfg(test)] mod lowered_variable_tests; #[cfg(test)] mod lowering_scope_tests; diff --git a/src/simlin-engine/src/db/interned_reuse_tests.rs b/src/simlin-engine/src/db/interned_reuse_tests.rs new file mode 100644 index 000000000..4212f113b --- /dev/null +++ b/src/simlin-engine/src/db/interned_reuse_tests.rs @@ -0,0 +1,248 @@ +// Copyright 2026 The Simlin Authors. All rights reserved. +// Use of this source code is governed by the Apache License, +// Version 2.0, that can be found in the LICENSE file. + +//! An edit history that reuses interned slots never reaches salsa's +//! validation assertion. +//! +//! Salsa collects an interned value no revision has used for a while and +//! reuses its slot for another. Reading a collectable value's fields, or a memo +//! keyed on it, without the value having been interned or validated in the +//! current revision is a panic (`Value::assert_validated` in salsa's +//! `interned.rs`), and a panic is a process abort in a release build. +//! +//! This database's keys are collectable: `LtmLinkId` and `ModuleInputSet` keep +//! salsa's default `revisions`, and so do the argument tuples salsa interns for +//! every query that takes more than one argument, which is most of them. What +//! keeps the assertion out of reach is that no memoized value holds an interned +//! id, so an id is only ever a query's key, a dependency edge, or a local under +//! the borrow that interned it. +//! +//! A database that lives for one compile never reuses a slot (collection starts +//! after the third revision that interns anything, and takes a slot nothing has +//! used for three), so the ordinary suite passing says nothing about this. +//! These tests drive the database the way a host does over many edits and count +//! salsa's own `DidReuseInternedValue` events: a history in which no slot was +//! reused fails, because it would not have exercised the property. + +use std::collections::BTreeMap; +use std::sync::{Arc, Mutex}; + +use salsa::{Database, Event, EventKind, IngredientIndex}; + +use super::{LtmOverlay, SimlinDb, collect_all_diagnostics, compile_project_incremental}; +use crate::datamodel; +use crate::test_common::TestProject; + +/// A database that records which ingredient each reused interned slot belongs +/// to. +fn db_recording_reuse() -> (SimlinDb, Arc>>) { + let reused: Arc>> = Arc::new(Mutex::new(Vec::new())); + let sink = Arc::clone(&reused); + let storage = salsa::Storage::new(Some(Box::new(move |event: Event| { + if let EventKind::DidReuseInternedValue { key, .. } = event.kind { + sink.lock() + .expect("reuse log poisoned") + .push(key.ingredient_index()); + } + }))); + (SimlinDb::with_storage(storage), reused) +} + +/// A model whose interned keys change with `round`: +/// +/// * the rate's NAME rotates, so the name-keyed projections +/// (`model_variable_by_name` and friends) and the `LtmLinkId`s of the links +/// through it are keys one round interns and a later one abandons; +/// * the smooth's call shape alternates between two and three arguments, and +/// a stdlib delay appears and disappears; +/// * an explicit module instance is wired to a pair of its sub-model's +/// `PORTS` ports that changes every round, so its bound-port set +/// (`ModuleInputSet`, part of the key of the dependency graph, every +/// fragment and the assembled module) is one of 55, and rounds keep +/// interning sets no earlier round did while the sets of rounds long past +/// sit unused. +fn churn_project(round: usize) -> datamodel::Project { + const PORTS: usize = 11; + let k = round % PORTS; + let rate = format!("rate_{k}"); + let perceived = if round.is_multiple_of(2) { + "SMTH1(pop, 3)".to_string() + } else { + "SMTH1(pop, 3, 50)".to_string() + }; + let mut builder = TestProject::new("churn") + .with_sim_time(0.0, 4.0, 0.5) + .stock("pop", "100", &["births"], &["deaths"], None) + .flow("births", &format!("pop * {rate}"), None) + .flow("deaths", "pop * crowding / 20", None) + .aux(&rate, &format!("0.0{} * (2 - crowding)", k + 1), None) + .aux("crowding", "(perceived + inst.out) / 2000", None) + .aux("perceived", &perceived, None); + if round.is_multiple_of(3) { + builder = builder + .aux(&format!("lagged_{k}"), "DELAY3(births, 2)", None) + .aux("readout", &format!("lagged_{k} + pop"), None); + } + let mut project = builder.build_datamodel(); + + let port = |n: usize| format!("p_{n}"); + let wired = [k, (k + 1 + (round / PORTS) % (PORTS - 1)) % PORTS]; + project.models[0] + .variables + .push(datamodel::Variable::Module(datamodel::Module { + ident: "inst".to_string(), + model_name: "sub".to_string(), + documentation: String::new(), + units: None, + references: wired + .iter() + .map(|n| datamodel::ModuleReference { + src: "pop".to_string(), + dst: format!("inst.{}", port(*n)), + }) + .collect(), + compat: datamodel::Compat::default(), + ai_state: None, + uid: None, + })); + + let sub_aux = |ident: String, equation: String, can_be_module_input: bool| { + datamodel::Variable::Aux(datamodel::Aux { + ident, + equation: datamodel::Equation::Scalar(equation), + documentation: String::new(), + units: None, + gf: None, + ai_state: None, + uid: None, + compat: datamodel::Compat { + can_be_module_input, + ..datamodel::Compat::default() + }, + }) + }; + let mut sub_vars: Vec = (0..PORTS) + .map(|n| sub_aux(port(n), "1".to_string(), true)) + .collect(); + let sum = (0..PORTS).map(port).collect::>().join(" + "); + sub_vars.push(sub_aux("out".to_string(), sum, false)); + project.models.push(datamodel::Model { + name: "sub".to_string(), + sim_specs: None, + variables: sub_vars.into(), + views: vec![], + loop_metadata: vec![], + groups: vec![], + macro_spec: None, + }); + project +} + +/// What a host asks of the db after an edit, with and without the LTM +/// overlay: the compiled simulation and every diagnostic. +fn query_as_a_host_does(db: &SimlinDb, project: super::SourceProject) { + for overlay in [LtmOverlay::Off, LtmOverlay::On] { + let compiled = compile_project_incremental(db, project, "main", overlay); + assert!( + compiled.is_ok(), + "the churn model compiles: {:?}", + compiled.err() + ); + let _ = collect_all_diagnostics(db, project, overlay); + } +} + +/// One edit, as a host makes it: the sync, the queries, then the memo release +/// libsimlin's `DbLock` runs when it drops. +fn edit(db: &mut SimlinDb, round: usize) { + let project = db.sync(&churn_project(round)); + query_as_a_host_does(db, project); + db.release_replaced_memos(); +} + +/// How many slots each ingredient reused, by the ingredient's name. +fn reuse_by_ingredient(db: &SimlinDb, reused: &[IngredientIndex]) -> BTreeMap { + let mut by_ingredient: BTreeMap = BTreeMap::new(); + for index in reused { + *by_ingredient + .entry(db.ingredient_debug_name(*index).into_owned()) + .or_default() += 1; + } + by_ingredient +} + +/// A short history, small enough for the default suite: enough edits for +/// salsa to start collecting, and for slots of the per-variable argument +/// tuples to be reused. +#[test] +fn a_few_edits_reuse_interned_slots_without_reaching_the_assertion() { + let (mut db, reused) = db_recording_reuse(); + for round in 0..12 { + edit(&mut db, round); + } + let reused = reused.lock().expect("reuse log poisoned"); + assert!( + !reused.is_empty(), + "no interned slot was reused, so this history says nothing about the assertion" + ); +} + +/// A long history, with both kinds of key this database interns reused: an +/// interned struct and the argument tuples of the central queries. +/// +/// Which slots a history reuses is not a property of the history alone. Salsa +/// keeps each interned table in `4 * available_parallelism` shards (rounded up +/// to a power of two), reuses a slot only for a key that hashes into the same +/// shard, and looks only at the shard's least-recently-interned entry +/// (`find_reusable_slot`); an entry a memo's verification revalidates is +/// refreshed where it sits (`maybe_changed_after`), so it can stand at that +/// end of a shard and hold the slots behind it for as long as it is +/// revalidated. The fewer the shards, the likelier that is. The ingredients +/// required below are the ones this history reuses at every shard count from +/// 4 to 128 (1 to 32 cores). `LtmLinkId` is not among them: this history +/// reuses its slots dozens of times at 64 shards and up, and once or never +/// below that, so on a small machine it exercises `LtmLinkId` as a live key +/// and not as a reused one. +/// +/// Its middle phase makes no input write at all. `release_replaced_memos` +/// opens a synthetic revision on every 256th consecutive call (salsa counts +/// exclusive accesses in a `u8`), so the keys the queries there read are +/// validated in revisions no edit opened, and the edits after it reuse the +/// slots that went stale across them. +#[test] +#[ignore = "240 edits and 1,536 quiet releases of a small model, queried under both overlays; run under the gates profile"] +fn an_edit_history_that_reuses_interned_slots_never_reaches_the_assertion() { + let (mut db, reused) = db_recording_reuse(); + + for round in 0..120 { + edit(&mut db, round); + } + + let project = db.sync(&churn_project(0)); + for _ in 0..(256 * 6) { + db.release_replaced_memos(); + query_as_a_host_does(&db, project); + } + + for round in 120..240 { + edit(&mut db, round); + } + + let reused = reused.lock().expect("reuse log poisoned"); + let by_ingredient = reuse_by_ingredient(&db, &reused); + // The keys whose reuse matters most: an explicit interned struct, and the + // argument tuples of the queries every compile goes through. A history + // that stops reusing one of them has stopped testing it. + for ingredient in [ + "ModuleInputSet", + "compile_var_fragment::interned_arguments", + "assemble_module::interned_arguments", + "variable_direct_dependencies::interned_arguments", + ] { + assert!( + by_ingredient.get(ingredient).copied().unwrap_or(0) > 0, + "no `{ingredient}` slot was reused; reused: {by_ingredient:#?}" + ); + } +} diff --git a/src/simlin-engine/src/db/ltm_module_tests.rs b/src/simlin-engine/src/db/ltm_module_tests.rs index 4e100ac9e..3cd2d5ec8 100644 --- a/src/simlin-engine/src/db/ltm_module_tests.rs +++ b/src/simlin-engine/src/db/ltm_module_tests.rs @@ -1291,11 +1291,10 @@ fn test_results_offsets_agree_with_layout_under_ltm() { ); } -/// C-LEARN-scale version of the offsets-vs-layout consistency check. -/// Ignored by default (loads a 1.4 MB model); run explicitly with -/// `cargo test -- --ignored test_clearn_results_offsets_agree_with_layout`. +/// C-LEARN-scale version of the offsets-vs-layout consistency check +/// (`scripts/gates.sh test_clearn_results_offsets_agree_with_layout`). #[test] -#[ignore] +#[ignore = "C-LEARN compiled under LTM, every results offset against the layout; run under the gates profile"] fn test_clearn_results_offsets_agree_with_layout() { use crate::db::flattened_offsets; use salsa::Setter; diff --git a/src/simlin-engine/src/db/ltm_tests.rs b/src/simlin-engine/src/db/ltm_tests.rs index 901a543ee..85c661720 100644 --- a/src/simlin-engine/src/db/ltm_tests.rs +++ b/src/simlin-engine/src/db/ltm_tests.rs @@ -895,12 +895,12 @@ fn unparseable_generated_arm_degrades_loudly_without_panicking() { /// /// `#[ignore]`d: this is the measuring instrument, not a gate. Checked in /// (rather than the numbers) so the measurement is reproducible -- run with -/// `cargo test -p simlin-engine --release --lib -- --ignored --nocapture -/// per_element_generation_scaling`. A timing assertion in the default suite -/// would be flaky and would risk the 3-minute wall-clock cap; the structural -/// guarantee is instead pinned by `slot_occurrence_index_groups_every_slot`. +/// `scripts/gates.sh --nocapture per_element_generation_scaling`. A timing +/// assertion in the default suite would be flaky and would risk the 3-minute +/// wall-clock cap; the structural guarantee is instead pinned by +/// `slot_occurrence_index_groups_every_slot`. #[test] -#[ignore] +#[ignore = "an instrument: prints link-score generation time per target width and asserts no timing; run under the gates profile"] fn per_element_generation_scaling() { use std::time::Instant; diff --git a/src/simlin-engine/src/lib.rs b/src/simlin-engine/src/lib.rs index f7d6b50a2..20bbcf792 100644 --- a/src/simlin-engine/src/lib.rs +++ b/src/simlin-engine/src/lib.rs @@ -118,8 +118,6 @@ pub mod systems; mod systems_stdlib_tests; #[cfg(any(test, feature = "test-support"))] pub mod test_common; -#[cfg(all(test, feature = "xmutil"))] -mod test_open_vensim; #[cfg(test)] mod test_sir_xmile; #[cfg(test)] @@ -161,8 +159,6 @@ pub use self::vm::{CompiledSimulation, Vm}; pub use self::vm_profile::BytecodeProfile; // Re-export compat functions at the crate root for convenience -#[cfg(feature = "xmutil")] -pub use self::compat::open_vensim_xmutil; #[cfg(feature = "file_io")] pub use self::compat::{load_csv, load_dat}; pub use self::compat::{ diff --git a/src/simlin-engine/src/mdl/CLAUDE.md b/src/simlin-engine/src/mdl/CLAUDE.md index 56da8700b..366632245 100644 --- a/src/simlin-engine/src/mdl/CLAUDE.md +++ b/src/simlin-engine/src/mdl/CLAUDE.md @@ -1,6 +1,6 @@ # Vensim MDL Parser -Pure Rust reader and writer of Vensim MDL files: it reads a file into a `datamodel::Project` and writes a project back as MDL. +Pure Rust reader and writer of Vensim MDL files: it reads a file into a `datamodel::Project` and writes a project back as MDL. Its reading algorithms are ported from xmutil, Bob Eberlein's C++ Vensim-to-XMILE converter (https://github.com/bobeberlein/xmutil), which is what a comment citing an xmutil function or `File.cpp:line` names. For global development standards, see the root [CLAUDE.md](/CLAUDE.md). For build/test/lint commands, see [docs/dev/commands.md](/docs/dev/commands.md). @@ -26,7 +26,7 @@ For the design and the Vensim format notes, see [docs/design/mdl-parser.md](/doc - **An `:EXCEPT:` default fills no element**: it applies only where every element has a slot, so an excepted element nothing defines stays undefined (the engine's rule, unverified). - **A number list defines the elements its left-hand side does, less those it excepts, one number each, in order** (`convert/number_list_tests.rs`). A list of another length, or one no element takes numbers from, is refused: which number an element would take is a guess. Whether Vensim refuses such a list is unverified. - **An element's name as a value is its position**, written `Dimension.Element` in the dimension that owns it, the largest (`xmile_compat`'s `element_value`, `convert::element_owners`; `convert/element_value_tests.rs`). - - **A single apply-to-all MDL equation imports as one `Equation::ApplyToAll`**, not as N identical per-element slots. A dimension name in an expression is the element's 1-based position, which only an apply-to-all equation has an active dimension for, so `y[DimA] = VECTOR ELM MAP(x[three], (DimA - 1))` compiles only in that form. The collapse is gated by `slots_are_one_apply_to_all` plus a `single_apply_to_all` precondition: ONE source equation with no `:EXCEPT:`; not backed by external data (the whole opaque `{GET ...}` family, `external_data::is_external_data_placeholder`: a resolvable one's slots agree only as a property of the spreadsheet, and an unresolvable one leaves an empty equation in every slot, which `ApplyToAll(dims, "")` turns into an `EmptyEquation` error); no slot with an INITIAL equation or a graphical function (`ApplyToAll` has nowhere to put either); and slots covering the dimensions' full cartesian product. The INITIAL, graphical-function and coverage clauses are each load-bearing (dropping one reds its own test); the `:EXCEPT:`/default/equation-text clauses are belt-and-braces, since `needs_substitution` is exactly `expanded_eqs.len() > 1 || has_except_eq`. The rule is a stricter form of the one [mdl_equivalence.rs](/src/simlin-engine/tests/integration/mdl_equivalence.rs)'s `normalize_equation` applies when comparing against xmutil (which collapses on slot agreement alone), so anything the importer collapses the harness does too. Rows: `convert/apply_to_all_tests.rs`; corpus gate `simulate::simulates_vector_mdl_genuine` (`vector.mdl` against real-Vensim `vector.dat`). + - **A single apply-to-all MDL equation imports as one `Equation::ApplyToAll`**, not as N identical per-element slots. A dimension name in an expression is the element's 1-based position, which only an apply-to-all equation has an active dimension for, so `y[DimA] = VECTOR ELM MAP(x[three], (DimA - 1))` compiles only in that form. The collapse is gated by `slots_are_one_apply_to_all` plus a `single_apply_to_all` precondition: ONE source equation with no `:EXCEPT:`; not backed by external data (the whole opaque `{GET ...}` family, `external_data::is_external_data_placeholder`: a resolvable one's slots agree only as a property of the spreadsheet, and an unresolvable one leaves an empty equation in every slot, which `ApplyToAll(dims, "")` turns into an `EmptyEquation` error); no slot with an INITIAL equation or a graphical function (`ApplyToAll` has nowhere to put either); and slots covering the dimensions' full cartesian product. The INITIAL, graphical-function and coverage clauses are each load-bearing (dropping one reds its own test); the `:EXCEPT:`/default/equation-text clauses are belt-and-braces, since `needs_substitution` is exactly `expanded_eqs.len() > 1 || has_except_eq`. Rows: `convert/apply_to_all_tests.rs`; corpus gate `simulate::simulates_vector_mdl_genuine` (`vector.mdl` against real-Vensim `vector.dat`). - **An empty right-hand side is no equation** (`""`), the same as a lookup-only table's. - `stocks.rs` -- Stock/flow linking via is_all_plus_minus algorithm and the synthetic net flow - `dimensions.rs` -- Dimension/subscript building with range expansion and `DimensionMapping` construction; `axis_dimension` and `smallest_dimension_holding`, the reader's dimension rule, which the writer asks too @@ -101,14 +101,12 @@ On every run, in one process or many: element ownership among dimensions of one - Name post-processing (`SpaceToUnderBar`, `MakeViewNamesUnique`) - Variable filtering (Time, ARRAY types in views) -- The C-LEARN differences from xmutil (`test_clearn_equivalence`; see the design doc) - `mdl::parser`'s binary precedence table (GH #914) ## Commands ```bash cargo test -p simlin-engine mdl:: # MDL-specific tests -cargo test --release -p simlin-engine mdl:: -- --ignored # the corpus gates -cargo test -p simlin-engine --features xmutil --test integration test_mdl_equivalence -- --nocapture # equivalence with xmutil -cargo test -p simlin-engine --features xmutil --test integration test_clearn_equivalence -- --ignored --nocapture # C-LEARN +scripts/gates.sh mdl:: # the corpus gates, optimized +cargo test -p simlin-engine --test integration simulate:: # The corpus against Vensim's output ``` diff --git a/src/simlin-engine/src/mdl/convert/apply_to_all_tests.rs b/src/simlin-engine/src/mdl/convert/apply_to_all_tests.rs index 72d2d0e3e..a0cf2cc7d 100644 --- a/src/simlin-engine/src/mdl/convert/apply_to_all_tests.rs +++ b/src/simlin-engine/src/mdl/convert/apply_to_all_tests.rs @@ -14,12 +14,11 @@ //! dimension-position arithmetic hits it, not just the `VECTOR ELM MAP` offset //! that surfaced it. //! -//! The rule implemented is the one the MDL equivalence harness already applies -//! when comparing against xmutil (`mdl_equivalence::normalize_equation`): slots -//! that are identical in equation text, initial text and graphical function ARE -//! an apply-to-all, so collapsing them loses nothing. The tests below are in two -//! halves -- shapes that must collapse, and shapes that must NOT, each named for -//! the reason it is per-element. +//! The rule implemented: slots that are identical in equation text, initial +//! text and graphical function ARE an apply-to-all, so collapsing them loses +//! nothing (it is also what xmutil emits for such an equation). The tests below +//! are in two halves -- shapes that must collapse, and shapes that must NOT, +//! each named for the reason it is per-element. use crate::datamodel::Equation; diff --git a/src/simlin-engine/src/mdl/convert/variables.rs b/src/simlin-engine/src/mdl/convert/variables.rs index 18feacd1e..609226b45 100644 --- a/src/simlin-engine/src/mdl/convert/variables.rs +++ b/src/simlin-engine/src/mdl/convert/variables.rs @@ -804,9 +804,7 @@ impl<'input> ConversionContext<'input> { // correct through our XMILE reader, failed to compile through this // one. Collapsing loses nothing by construction (see // `slots_are_one_apply_to_all` for the two things `ApplyToAll` - // cannot carry), and it is the rule the MDL equivalence harness - // already applies to compare us with xmutil, which emits - // apply-to-all here. + // cannot carry), and it is what xmutil emits here. Equation::ApplyToAll(formatted_dims.clone(), elements[0].1.clone()) } else { Equation::Arrayed( diff --git a/src/simlin-engine/src/mdl/mod.rs b/src/simlin-engine/src/mdl/mod.rs index b4bc78b63..032d364c8 100644 --- a/src/simlin-engine/src/mdl/mod.rs +++ b/src/simlin-engine/src/mdl/mod.rs @@ -5,8 +5,7 @@ //! Vensim MDL file parser. //! //! This module provides a pure Rust implementation for parsing Vensim MDL files -//! directly into `crate::datamodel::Project` structures, replacing the -//! C++ xmutil dependency. +//! directly into `crate::datamodel::Project` structures. //! //! See `CLAUDE.md` in this directory for implementation context and goals. diff --git a/src/simlin-engine/src/test_common.rs b/src/simlin-engine/src/test_common.rs index 7ce383aa6..202942907 100644 --- a/src/simlin-engine/src/test_common.rs +++ b/src/simlin-engine/src/test_common.rs @@ -1304,17 +1304,27 @@ pub fn nested_instance_arrayed_submodel_project() -> Project { } /// The series [`nested_instance_arrayed_submodel_project`] must produce. +/// +/// The names are literals rather than formatted and leaked into `'static`: +/// the suite runs under a leak checker (`test-asan.sh`), where a leak is a +/// failure whoever made it. pub fn nested_instance_arrayed_submodel_expected() -> Vec<(&'static str, Vec)> { - let p = |instance: &str, var: &str| -> &'static str { - Box::leak(format!("m_{instance}\u{b7}inr\u{b7}{var}").into_boxed_str()) - }; vec![ - (p("a", "out_curr"), vec![70.0, 140.0, 210.0, 280.0]), - (p("b", "out_curr"), vec![7000.0, 14000.0, 21000.0, 28000.0]), - (p("a", "out_prev"), vec![0.0, 70.0, 140.0, 210.0]), - (p("b", "out_prev"), vec![0.0, 7000.0, 14000.0, 21000.0]), - (p("a", "out_init"), vec![70.0; 4]), - (p("b", "out_init"), vec![7000.0; 4]), + ( + "m_a\u{b7}inr\u{b7}out_curr", + vec![70.0, 140.0, 210.0, 280.0], + ), + ( + "m_b\u{b7}inr\u{b7}out_curr", + vec![7000.0, 14000.0, 21000.0, 28000.0], + ), + ("m_a\u{b7}inr\u{b7}out_prev", vec![0.0, 70.0, 140.0, 210.0]), + ( + "m_b\u{b7}inr\u{b7}out_prev", + vec![0.0, 7000.0, 14000.0, 21000.0], + ), + ("m_a\u{b7}inr\u{b7}out_init", vec![70.0; 4]), + ("m_b\u{b7}inr\u{b7}out_init", vec![7000.0; 4]), ] } diff --git a/src/simlin-engine/src/test_open_vensim.rs b/src/simlin-engine/src/test_open_vensim.rs deleted file mode 100644 index 94be4c869..000000000 --- a/src/simlin-engine/src/test_open_vensim.rs +++ /dev/null @@ -1,37 +0,0 @@ -// Copyright 2026 The Simlin Authors. All rights reserved. -// Use of this source code is governed by the Apache License, -// Version 2.0, that can be found in the LICENSE file. - -#[cfg(test)] -mod tests { - use crate::open_vensim_xmutil; - use std::fs; - - #[test] - fn test_open_vensim_xmutil_sir() { - let mdl_path = "../libsimlin/testdata/SIR.mdl"; - let mdl_content = fs::read_to_string(mdl_path).unwrap(); - - let project = open_vensim_xmutil(&mdl_content) - .expect("open_vensim_xmutil should successfully parse SIR.mdl"); - - // Validate the project has expected structure - assert!( - !project.models.is_empty(), - "Project should have at least one model" - ); - - // Check that the main model exists - let main_model = project - .models - .iter() - .find(|m| m.name == "main") - .expect("Project should have a main model"); - - // Validate the model has variables - assert!( - !main_model.variables.is_empty(), - "Main model should have variables" - ); - } -} diff --git a/src/simlin-engine/src/tools/battery_tests.rs b/src/simlin-engine/src/tools/battery_tests.rs index c9e3fb050..e0c954184 100644 --- a/src/simlin-engine/src/tools/battery_tests.rs +++ b/src/simlin-engine/src/tools/battery_tests.rs @@ -4488,8 +4488,7 @@ fn check_labeled_corpus(include: impl Fn(&LabeledModel) -> bool) -> (usize, usiz /// the labeled corpus, every listed check that did not pass has been judged, /// and the ones a modeler would not accept are few. /// -/// Run with: cargo test --release -p simlin-engine --lib -- --ignored -/// the_batterys_findings_over_the_corpus_are_ones_a_modeler_accepts +/// Run with: scripts/gates.sh the_batterys_findings_over_the_corpus_are_ones_a_modeler_accepts #[test] #[ignore = "runs the battery on 29 corpus models, World3 and C-LEARN among them; run under the \ gates profile"] diff --git a/src/simlin-engine/src/tools/corpus_tests.rs b/src/simlin-engine/src/tools/corpus_tests.rs index a0c4bd872..7daf9ab31 100644 --- a/src/simlin-engine/src/tools/corpus_tests.rs +++ b/src/simlin-engine/src/tools/corpus_tests.rs @@ -9,8 +9,7 @@ //! corpus has what no fixture was made for -- a series that goes undefined, a //! Vensim macro, a model too large to enumerate. //! -//! Run with: cargo test --release -p simlin-engine --lib tools::corpus_tests -//! -- --ignored --nocapture +//! Run with: scripts/gates.sh --nocapture tools::corpus_tests use std::collections::HashMap; use std::path::{Path, PathBuf}; @@ -335,7 +334,7 @@ fn sweep_model(sweep: &mut Sweep, display: &str, project: datamodel::Project) { } #[test] -#[ignore = "every corpus model through every tool: minutes on a debug build"] +#[ignore = "every corpus model through every tool; run under the gates profile"] fn every_corpus_answer_matches_its_schema_and_fits_the_budget() { let root = Path::new(env!("CARGO_MANIFEST_DIR")).join("../../"); let mut sweep = Sweep::new(); diff --git a/src/simlin-engine/src/tools/loops_tests.rs b/src/simlin-engine/src/tools/loops_tests.rs index b8e84ee18..b66317ab5 100644 --- a/src/simlin-engine/src/tools/loops_tests.rs +++ b/src/simlin-engine/src/tools/loops_tests.rs @@ -977,10 +977,9 @@ fn a_loops_polarity_reads_the_same_from_a_run_and_from_structure() { /// The corpus's two largest models' loops are analyzed within a budget of /// time and size, each call timed. /// -/// Run with: cargo test --release -p simlin-engine --features schema --lib -- -/// --ignored the_largest_corpus_models_loops_are_analyzed_within_bounds --nocapture +/// Run with: scripts/gates.sh --nocapture the_largest_corpus_models_loops_are_analyzed_within_bounds #[test] -#[ignore = "compiles World3 and C-LEARN under the LTM overlay: seconds on a debug build"] +#[ignore = "compiles World3 and C-LEARN under the LTM overlay; run under the gates profile"] fn the_largest_corpus_models_loops_are_analyzed_within_bounds() { for path in [ "../../test/metasd/WRLD3-03/wrld3-03.mdl", diff --git a/src/simlin-engine/src/tools/outline_tests.rs b/src/simlin-engine/src/tools/outline_tests.rs index 800be5242..731b3d8e8 100644 --- a/src/simlin-engine/src/tools/outline_tests.rs +++ b/src/simlin-engine/src/tools/outline_tests.rs @@ -347,10 +347,9 @@ fn a_sector_outline_over_the_budget_lists_the_stocks_that_fit_and_counts_the_res /// The corpus's two largest models are outlined within the budget, and a read /// of their first stocks answers, each call timed. /// -/// Run with: cargo test --release -p simlin-engine --lib -- --ignored -/// the_largest_corpus_models_are_outlined_within_the_budget --nocapture +/// Run with: scripts/gates.sh --nocapture the_largest_corpus_models_are_outlined_within_the_budget #[test] -#[ignore = "compiles World3 and C-LEARN for their diagnostics: several seconds on a debug build"] +#[ignore = "compiles World3 and C-LEARN for their diagnostics; run under the gates profile"] fn the_largest_corpus_models_are_outlined_within_the_budget() { for path in [ "../../test/metasd/WRLD3-03/wrld3-03.mdl", diff --git a/src/simlin-engine/tests/integration/clearn_unit_errors.rs b/src/simlin-engine/tests/integration/clearn_unit_errors.rs index cb5e9b0af..253ceed57 100644 --- a/src/simlin-engine/tests/integration/clearn_unit_errors.rs +++ b/src/simlin-engine/tests/integration/clearn_unit_errors.rs @@ -9,7 +9,7 @@ //! "template" so we can see WHAT kinds of unit errors Simlin emits and how //! many of each. Run with: //! -//! cargo test -p simlin-engine --test integration -- --ignored --nocapture +//! cargo test -p simlin-engine --test integration clearn_unit_errors -- --nocapture use simlin_engine::common::UnitError; use simlin_engine::db::{ diff --git a/src/simlin-engine/tests/integration/ltm_discovery_large_models.rs b/src/simlin-engine/tests/integration/ltm_discovery_large_models.rs index f6b316ece..e3930c5f0 100644 --- a/src/simlin-engine/tests/integration/ltm_discovery_large_models.rs +++ b/src/simlin-engine/tests/integration/ltm_discovery_large_models.rs @@ -505,14 +505,11 @@ fn discovery_contract_holds_on_tractable_arrayed_model() { /// out of scope here and would be tracked separately. /// /// `#[ignore]`d for the `cargo test --workspace` time budget: parsing -/// C-LEARN's 1.4 MB MDL plus a discovery-mode compile runs several -/// seconds in release and proportionally longer in the debug build CI -/// uses, so on-demand execution is appropriate. Run explicitly with: -/// cargo test --release -p simlin-engine \ -/// --test integration -- --ignored --nocapture \ -/// clearn_ltm_discovery_compiles +/// C-LEARN's 1.4 MB MDL plus a discovery-mode compile runs about a second +/// optimized and 15 s on a debug build. Run with: +/// scripts/gates.sh --nocapture clearn_ltm_discovery_compiles #[test] -#[ignore] +#[ignore = "C-LEARN compiled in LTM discovery mode; run under the gates profile"] fn clearn_ltm_discovery_compiles() { let mdl = match std::fs::read_to_string(CLEARN_MDL) { Ok(contents) => contents, @@ -546,11 +543,12 @@ fn clearn_ltm_discovery_compiles() { /// dominance yet were entirely absent from the sampled report -- must be /// present. /// -/// `#[ignore]`d for runtime class only (a full 401-step simulate plus the -/// ~150k-circuit enumeration and its scoring passes run minutes in a debug -/// build); run with `cargo test --release -- --ignored`. +/// `#[ignore]`d for runtime only: a full 401-step simulate plus the +/// ~150k-circuit enumeration and its scoring passes, well over the per-test +/// budget on a debug build. Run with +/// `scripts/gates.sh world3_full_run_enumeration_is_complete`. #[test] -#[ignore] +#[ignore = "World3 simulated under LTM and every circuit enumerated and scored; run under the gates profile"] fn world3_full_run_enumeration_is_complete() { let inputs = world3_discovery_inputs(); let found = ltm_finding::discover_loops_with_graph( diff --git a/src/simlin-engine/tests/integration/main.rs b/src/simlin-engine/tests/integration/main.rs index 0f4b0adae..587ce8ecb 100644 --- a/src/simlin-engine/tests/integration/main.rs +++ b/src/simlin-engine/tests/integration/main.rs @@ -17,12 +17,11 @@ //! //! Feature gating: `file_io` is always on for this harness -- the crate takes a //! self dev-dependency that enables it (see Cargo.toml), because `file_io` -//! gates a production dependency, not test coverage (GH #925). `xmutil` gates a -//! heavyweight optional C++ converter that only one module compares against, so -//! that one module stays `#[cfg]`-gated below. A `cfg`-gated module compiles to -//! nothing when its feature is off, which has the same effect as the -//! `required-features` entries these replaced (the tests only exist when the -//! feature is enabled) without skipping the whole harness. +//! gates a production dependency, not test coverage (GH #925). No module here +//! needs another feature. One that comes to need one gates itself with +//! `#[cfg(feature = ...)]` on its `mod` line rather than taking a +//! `required-features` entry, which would skip the whole harness when the +//! feature is off. mod test_helpers; @@ -42,10 +41,6 @@ mod ltm_integration_method; mod ltm_loop_nodes; mod ltm_relative_scores; mod ltm_snapshot_inputs; -// Compares xmutil-based MDL parsing against the native Rust parser, so it -// needs the optional xmutil C++ converter compiled in. -#[cfg(feature = "xmutil")] -mod mdl_equivalence; mod mdl_roundtrip; mod mdl_vensim_truth; mod metasd_macros; diff --git a/src/simlin-engine/tests/integration/mdl_equivalence.rs b/src/simlin-engine/tests/integration/mdl_equivalence.rs deleted file mode 100644 index 08cf2c420..000000000 --- a/src/simlin-engine/tests/integration/mdl_equivalence.rs +++ /dev/null @@ -1,1327 +0,0 @@ -// Copyright 2026 The Simlin Authors. All rights reserved. -// Use of this source code is governed by the Apache License, -// Version 2.0, that can be found in the LICENSE file. - -//! MDL equivalence tests comparing xmutil-based parsing vs native Rust parsing. -//! -//! These tests verify that `open_vensim_xmutil()` (MDL -> xmutil -> XMILE -> datamodel) produces -//! equivalent results to `open_vensim()` (MDL -> datamodel directly). -//! -//! ## Scope Notes -//! -//! - View/diagram parsing is implemented in the native parser and validated in -//! `assert_model_equivalent`. -//! - `Model.loop_metadata` is intentionally ignored in equivalence checks because -//! the xmutil -> XMILE -> datamodel path currently does not preserve loop metadata. -//! - For Vensim MDL inputs, both paths currently use project-level `sim_specs`. -//! Model-level `sim_specs` are not populated by xmutil-generated XMILE. - -use std::collections::{HashMap, HashSet}; -use std::fs; - -use simlin_engine::canonicalize; -use simlin_engine::datamodel::{ - Aux, Dimension, DimensionElements, Dt, Equation, Flow, GraphicalFunction, Model, Module, - Project, SimSpecs, Stock, Variable, View, ViewElement, view_element, -}; -use simlin_engine::{open_vensim, open_vensim_xmutil}; - -/// Models that should produce equivalent output from both parsers. -/// Add models here as they pass equivalence testing. -static EQUIVALENT_MODELS: &[&str] = &[ - // SIR model - known to work - "src/libsimlin/testdata/SIR.mdl", - // SDEverywhere models - "test/sdeverywhere/models/active_initial/active_initial.mdl", - "test/sdeverywhere/models/sir/sir.mdl", - "test/sdeverywhere/models/comments/comments.mdl", - "test/sdeverywhere/models/delay/delay.mdl", - "test/sdeverywhere/models/elmcount/elmcount.mdl", - "test/sdeverywhere/models/index/index.mdl", - "test/sdeverywhere/models/initial/initial.mdl", - "test/sdeverywhere/models/pulsetrain/pulsetrain.mdl", - "test/sdeverywhere/models/smooth/smooth.mdl", - // "test/sdeverywhere/models/smooth3/smooth3.mdl", // causes segfault in xmutil - "test/sdeverywhere/models/specialchars/specialchars.mdl", - // "test/sdeverywhere/models/subalias/subalias.mdl", // dimension mapping handling differs - // "test/sdeverywhere/models/trend/trend.mdl", // causes segfault in xmutil - // test-models samples - "test/test-models/samples/SIR/SIR.mdl", - "test/test-models/samples/teacup/teacup.mdl", -]; - -// ===== MODELS WITH KNOWN ISSUES ===== -// Track failures with comments explaining the issue. -// -// Subscript handling in expressions differs (xmutil preserves "c[dima]", -// native uses "c"): -// "test/sdeverywhere/models/lookup/lookup.mdl" -// -// Uses ALLOCATE AVAILABLE builtin: -// "test/sdeverywhere/models/allocate/allocate.mdl" -// -// Uses GET DIRECT CONSTANTS: -// "test/sdeverywhere/models/arrays_cname/arrays_cname.mdl" -// "test/sdeverywhere/models/arrays_varname/arrays_varname.mdl" -// -// Uses DELAY FIXED builtin: -// "test/sdeverywhere/models/delayfixed/delayfixed.mdl" -// "test/sdeverywhere/models/delayfixed2/delayfixed2.mdl" -// -// Uses GET XLS DATA / GET DIRECT DATA: -// "test/sdeverywhere/models/directdata/directdata.mdl" -// "test/sdeverywhere/models/extdata/extdata.mdl" -// -// Uses GET DIRECT LOOKUPS: -// "test/sdeverywhere/models/directlookups/directlookups.mdl" -// -// Uses GET DIRECT SUBSCRIPT: -// "test/sdeverywhere/models/directsubs/directsubs.mdl" -// -// Uses EXCEPT syntax with subscript mappings: -// "test/sdeverywhere/models/except/except.mdl" -// "test/sdeverywhere/models/except2/except2.mdl" -// -// Uses GET DATA BETWEEN TIMES: -// "test/sdeverywhere/models/getdata/getdata.mdl" -// -// Uses subscript mappings: -// "test/sdeverywhere/models/mapping/mapping.mdl" -// "test/sdeverywhere/models/multimap/multimap.mdl" -// "test/sdeverywhere/models/subscript/subscript.mdl" -// -// Uses NPV builtin: -// "test/sdeverywhere/models/npv/npv.mdl" -// -// Uses QUANTUM builtin: -// "test/sdeverywhere/models/quantum/quantum.mdl" -// -// Uses SAMPLE IF TRUE builtin: -// "test/sdeverywhere/models/sample/sample.mdl" -// -// Preprocessing test files (no expected results): -// "test/sdeverywhere/models/flatten/expected.mdl" -// "test/sdeverywhere/models/flatten/input1.mdl" -// "test/sdeverywhere/models/preprocess/expected.mdl" -// "test/sdeverywhere/models/preprocess/input.mdl" - -/// Check if a model name is a stdlib model (used for builtins like SMOOTH3, TREND). -fn is_stdlib_model(name: &str) -> bool { - // Stdlib models have names starting with special Unicode characters - name.starts_with('∫') || name.starts_with('⌈') -} - -/// Normalize a dimension by lowercasing names (Vensim is case-insensitive). -fn normalize_dimension(dim: &mut Dimension) { - dim.name = dim.name.to_lowercase(); - if let DimensionElements::Named(elements) = &mut dim.elements { - for elem in elements.iter_mut() { - *elem = elem.to_lowercase(); - } - } - for mapping in &mut dim.mappings { - mapping.target = mapping.target.to_lowercase(); - } -} - -/// Normalize sim_specs by handling differences in save_step representation. -/// When save_step equals dt, some parsers set it to None while others set it explicitly. -fn normalize_sim_specs(specs: &mut SimSpecs) { - // If save_step equals dt, normalize to None (xmutil behavior) - if let Some(save_step) = &specs.save_step { - let dt_val = match &specs.dt { - Dt::Dt(v) => *v, - Dt::Reciprocal(v) => 1.0 / *v, - }; - let save_val = match save_step { - Dt::Dt(v) => *v, - Dt::Reciprocal(v) => 1.0 / *v, - }; - if (dt_val - save_val).abs() < 1e-10 { - specs.save_step = None; - } - } -} - -/// Normalize a project for comparison by clearing/sorting fields that may legitimately differ. -fn normalize_project(mut project: Project) -> Project { - // Project name differs: xmutil extracts from filename, native may leave empty - project.name = String::new(); - - // Source is implementation-dependent - project.source = None; - - // AI information is not relevant to MDL parsing - project.ai_information = None; - - // Normalize sim_specs - normalize_sim_specs(&mut project.sim_specs); - - // Unit equivalences match without normalization - - // Normalize dimensions (lowercase names for case-insensitive comparison) - for dim in &mut project.dimensions { - normalize_dimension(dim); - } - - // Normalize maps_to: xmutil sometimes sets maps_to to a subrange - // dimension instead of the parent dimension. Build element ownership - // and dimension containment maps to resolve to the largest parent. - { - // element -> owning dimension (largest dimension wins) - let mut elem_to_dim: HashMap = HashMap::new(); - let mut dims_by_size: Vec<(&str, Vec<&str>)> = project - .dimensions - .iter() - .filter_map(|d| { - if let DimensionElements::Named(elems) = &d.elements { - Some((d.name.as_str(), elems.iter().map(|e| e.as_str()).collect())) - } else { - None - } - }) - .collect(); - dims_by_size.sort_by_key(|(_, elems)| std::cmp::Reverse(elems.len())); - for (dim_name, elems) in &dims_by_size { - for elem in elems { - elem_to_dim - .entry(elem.to_string()) - .or_insert_with(|| dim_name.to_string()); - } - } - - // subrange dimension -> parent dimension - let mut dim_to_parent: HashMap = HashMap::new(); - for (dim_name, elems) in &dims_by_size { - if let Some(owner) = elems.first().and_then(|e| elem_to_dim.get(*e)) - && *owner != *dim_name - { - let all_same = elems - .iter() - .all(|e| elem_to_dim.get(*e).map(|o| o == owner).unwrap_or(false)); - if all_same { - dim_to_parent.insert(dim_name.to_string(), owner.clone()); - } - } - } - - for dim in &mut project.dimensions { - for mapping in &mut dim.mappings { - let target = &mapping.target; - // If target is a subrange dimension, resolve to its parent - if let Some(parent) = dim_to_parent.get(target.as_str()) { - mapping.target = parent.clone(); - } - // If target is an element, resolve to its owning dimension - else if let Some(parent_dim) = elem_to_dim - .get(target.as_str()) - .filter(|pd| *pd != &dim.name) - { - mapping.target = parent_dim.clone(); - } - } - } - } - - // Sort dimensions by name for consistent comparison - project.dimensions.sort_by(|a, b| a.name.cmp(&b.name)); - - // Filter out stdlib models and normalize the rest - project.models.retain(|m| !is_stdlib_model(&m.name)); - for model in &mut project.models { - normalize_model(model); - } - // Model ordering matches without sorting - - project -} - -/// Summarize view elements for debugging. -fn summarize_view_elements(views: &[View]) -> String { - if views.is_empty() { - return "no views".to_string(); - } - let View::StockFlow(sf) = &views[0]; - let mut counts: std::collections::HashMap<&str, usize> = std::collections::HashMap::new(); - for elem in &sf.elements { - let kind = match elem { - ViewElement::Aux(_) => "aux", - ViewElement::Stock(_) => "stock", - ViewElement::Flow(_) => "flow", - ViewElement::Link(_) => "link", - ViewElement::Module(_) => "module", - ViewElement::Alias(_) => "alias", - ViewElement::Cloud(_) => "cloud", - ViewElement::Group(_) => "group", - }; - *counts.entry(kind).or_insert(0) += 1; - } - let mut parts: Vec<_> = counts.iter().map(|(k, v)| format!("{}={}", k, v)).collect(); - parts.sort(); - format!("{} elements ({})", sf.elements.len(), parts.join(", ")) -} - -/// Normalize views for comparison. -/// Views are compared structurally in `assert_model_equivalent`; this helper is -/// only for debug summaries during test execution. -fn normalize_views(views: &mut [View]) { - if !views.is_empty() { - eprintln!(" {}", summarize_view_elements(views)); - } -} - -/// Normalize a model for comparison. -fn normalize_model(model: &mut Model) { - // Normalize views for comparison - normalize_views(&mut model.views); - - // Loop metadata is intentionally excluded from xmutil equivalence checks: - // the xmutil -> XMILE conversion path does not carry it through. - model.loop_metadata.clear(); - - // Normalize each variable - model.variables.rewrite(|variables| { - for var in variables.iter_mut() { - normalize_variable(var); - } - }); - - // Normalize synthetic net flows: xmutil and native may differ on whether - // to synthesize a net flow for a stock or decompose its rate expression - // into individual flows. Remove synthetic net flow variables and clear - // the affected stocks' inflows/outflows so the comparison focuses on - // the equation content (which is identical either way). - normalize_synthetic_flows(model); - - // Variable ordering matches without sorting -} - -/// Normalize synthetic net flows away. -/// -/// A "synthetic net flow" is a variable whose ident ends with `_net_flow` -/// that was generated to wrap a stock's rate expression. The xmutil and -/// native parsers may disagree on when to synthesize vs. decompose; -/// normalizing both sides removes the synthetic variables and clears all -/// stock inflow/outflow lists so the comparison focuses on the equation -/// content (which is semantically identical either way). -fn normalize_synthetic_flows(model: &mut Model) { - let has_synthetics = model - .variables - .iter() - .any(|v| v.get_ident().ends_with("_net_flow")); - - if !has_synthetics { - return; - } - - let synthetic_idents: HashSet = model - .variables - .iter() - .filter_map(|v| { - let id = v.get_ident(); - if id.ends_with("_net_flow") { - Some(id.to_string()) - } else { - None - } - }) - .collect(); - - // Collect all flow idents referenced from stock inflows/outflows BEFORE - // clearing, so we can decide which Flows are unambiguous (referenced in - // at least one stock on this side). - let all_stock_flow_idents: HashSet = model - .variables - .iter() - .filter_map(|v| { - if let Variable::Stock(stock) = v { - Some( - stock - .inflows - .iter() - .chain(stock.outflows.iter()) - .filter(|f| !synthetic_idents.contains(*f)) - .cloned() - .collect::>(), - ) - } else { - None - } - }) - .flatten() - .collect(); - - // Flow idents referenced in stocks that do NOT overlap with any - // synthetic-flow stock are considered unambiguous. - let affected_stocks: HashSet = synthetic_idents - .iter() - .filter_map(|s| s.strip_suffix("_net_flow").map(|stem| stem.to_string())) - .collect(); - - // Collect idents of flows used only by affected stocks - let mut affected_flow_idents: HashSet = HashSet::new(); - for var in &model.variables { - if let Variable::Stock(stock) = var - && (affected_stocks.contains(&stock.ident) - || stock.inflows.iter().any(|f| synthetic_idents.contains(f))) - { - for f in stock.inflows.iter().chain(stock.outflows.iter()) { - if !synthetic_idents.contains(f) { - affected_flow_idents.insert(f.clone()); - } - } - } - } - - // Unambiguous = referenced as a flow in some stock AND not only in affected stocks - let unambiguous_flow_idents: HashSet = all_stock_flow_idents - .difference(&affected_flow_idents) - .cloned() - .collect(); - - // Clear inflows/outflows for ALL stocks and drop synthetic variables. - model.variables.rewrite(|variables| { - for var in variables.iter_mut() { - if let Variable::Stock(stock) = var { - stock.inflows.clear(); - stock.outflows.clear(); - } - } - }); - model - .variables - .retain(|v| !synthetic_idents.contains(v.get_ident())); - - // Demote Flow -> Aux for variables that are only flows because of - // the synthesis-ambiguous stock. This ensures both parsers agree. - model.variables.rewrite(|variables| { - for var in variables.iter_mut() { - if let Variable::Flow(flow) = var - && !unambiguous_flow_idents.contains(&flow.ident) - { - *var = Variable::Aux(Aux { - ident: flow.ident.clone(), - equation: flow.equation.clone(), - documentation: flow.documentation.clone(), - units: flow.units.clone(), - gf: flow.gf.clone(), - ai_state: flow.ai_state, - uid: flow.uid, - compat: flow.compat.clone(), - }); - } - } - }); -} - -/// Clear view-related and AI-related fields from a variable. -fn normalize_variable(var: &mut Variable) { - match var { - Variable::Stock(stock) => normalize_stock(stock), - Variable::Flow(flow) => normalize_flow(flow), - Variable::Aux(aux) => normalize_aux(aux), - Variable::Module(module) => normalize_module(module), - } -} - -/// Canonicalize an identifier to lowercase with underscores. -fn canonical_ident(ident: &str) -> String { - canonicalize(ident).into_owned() -} - -/// Normalize documentation by removing line continuations and collapsing whitespace. -fn normalize_doc(doc: &str) -> String { - // Remove Vensim line continuations: backslash followed by CR/LF and tabs - let doc = doc.replace("\\\r\n", " "); - let doc = doc.replace("\\\n", " "); - // Collapse multiple whitespace (tabs, spaces) into single space - let doc: String = doc.split_whitespace().collect::>().join(" "); - doc -} - -/// Normalize units by removing spaces and underscores. -/// -/// The production code in `format_unit_expr` now handles simplification and -/// canonical formatting, so this only needs to normalize whitespace differences. -fn normalize_units(units: Option<&String>) -> Option { - units.map(|u| u.replace([' ', '_'], "")) -} - -/// Normalize an equation expression by: -/// - Lowercasing (function names and variable references) -/// - Removing spaces around operators -/// - Normalizing `:na:` to `nan` (xmutil preserves MDL syntax, native emits NAN) -fn normalize_expr(expr: &str) -> String { - // Lowercase the entire expression for case-insensitive comparison - // (Vensim is case-insensitive, xmutil lowercases, native may preserve) - let expr = expr.to_lowercase(); - // Remove spaces around operators: *, /, +, -, ^ - let expr = expr.replace(" * ", "*"); - let expr = expr.replace(" / ", "/"); - let expr = expr.replace(" + ", "+"); - let expr = expr.replace(" - ", "-"); - let expr = expr.replace(" ^ ", "^"); - // xmutil preserves MDL's :NA: literal in its XMILE output; the native - // parser correctly converts Expr::Na to NAN. Both are semantically - // identical, so normalize to the same form. - let expr = expr.replace(":na:", "nan"); - expr.trim().to_string() -} - -/// Normalize an equation. -/// For arrayed equations, sorts elements by subscript key to handle ordering differences -/// between native (sorted by subscript) and XMILE (preserves source order). -/// Also converts Arrayed equations where all elements have the same expression to ApplyToAll. -fn normalize_equation(eq: &mut Equation) { - match eq { - Equation::Scalar(expr) => { - *expr = normalize_expr(expr); - } - Equation::ApplyToAll(dims, expr) => { - // Lowercase and sort dimension names for consistent comparison - for dim in dims.iter_mut() { - *dim = dim.to_lowercase(); - } - dims.sort(); - *expr = normalize_expr(expr); - } - Equation::Arrayed(dims, elements, _default_eq, _) => { - // Lowercase and sort dimension names for consistent comparison - for dim in dims.iter_mut() { - *dim = dim.to_lowercase(); - } - dims.sort(); - // Normalize each element - for (subscript, expr, initial_comment, gf) in elements.iter_mut() { - // Canonicalize subscript names - *subscript = canonical_ident(subscript); - *expr = normalize_expr(expr); - if let Some(c) = initial_comment { - *c = normalize_expr(c); - } - // Graphical functions match without normalization - let _ = gf; - } - // Sort elements by canonicalized subscript key for order-independent - // comparison. The native parser and xmutil may produce different - // element orderings; this is not a semantic difference. - elements.sort_by(|a, b| a.0.cmp(&b.0)); - - // Check if all elements have the same expression - if so, convert to ApplyToAll - // This handles the case where native uses Arrayed with repeated equations - // while xmutil uses ApplyToAll - if !elements.is_empty() { - let first_expr = &elements[0].1; - let first_initial = &elements[0].2; - let all_same = elements.iter().all(|(_, e, init, gf)| { - e == first_expr && init == first_initial && gf.is_none() - }); - if all_same { - *eq = Equation::ApplyToAll(dims.clone(), first_expr.clone()); - } - } - } - } -} - -#[test] -fn test_normalize_arrayed_to_apply_to_all_preserves_initial() { - let mut eq = Equation::Arrayed( - vec!["dim".to_string()], - vec![ - ( - "a".to_string(), - "x+1".to_string(), - Some("INIT_VAL".to_string()), - None, - ), - ( - "b".to_string(), - "x+1".to_string(), - Some("INIT_VAL".to_string()), - None, - ), - ], - None, - false, - ); - normalize_equation(&mut eq); - // After normalization, expressions are lowercased and spaces around operators - // are removed. active_initial now lives in Compat, not in Equation. - assert_eq!( - eq, - Equation::ApplyToAll(vec!["dim".to_string()], "x+1".to_string(),) - ); -} - -fn normalize_stock(stock: &mut Stock) { - // Canonicalize identifier - stock.ident = canonical_ident(&stock.ident); - // Normalize documentation - stock.documentation = normalize_doc(&stock.documentation); - // Normalize units - stock.units = normalize_units(stock.units.as_ref()); - // Normalize equation - normalize_equation(&mut stock.equation); - // Normalize compat active_initial as an expression - if let Some(c) = stock.compat.active_initial.as_mut() { - *c = normalize_expr(c); - } - // uid is view-related - stock.uid = None; - // AI state is not relevant to MDL parsing equivalence - stock.ai_state = None; - // Canonicalize inflows/outflows (ordering matches without sorting) - stock.inflows = stock.inflows.iter().map(|s| canonical_ident(s)).collect(); - stock.outflows = stock.outflows.iter().map(|s| canonical_ident(s)).collect(); -} - -fn normalize_flow(flow: &mut Flow) { - flow.ident = canonical_ident(&flow.ident); - flow.documentation = normalize_doc(&flow.documentation); - flow.units = normalize_units(flow.units.as_ref()); - normalize_equation(&mut flow.equation); - // Normalize compat active_initial as an expression - if let Some(c) = flow.compat.active_initial.as_mut() { - *c = normalize_expr(c); - } - // Graphical functions match without normalization - flow.uid = None; - flow.ai_state = None; -} - -fn normalize_aux(aux: &mut Aux) { - aux.ident = canonical_ident(&aux.ident); - aux.documentation = normalize_doc(&aux.documentation); - aux.units = normalize_units(aux.units.as_ref()); - normalize_equation(&mut aux.equation); - // Normalize compat active_initial as an expression - if let Some(c) = aux.compat.active_initial.as_mut() { - *c = normalize_expr(c); - } - // Graphical functions match without normalization - aux.uid = None; - aux.ai_state = None; -} - -fn normalize_module(module: &mut Module) { - module.ident = canonical_ident(&module.ident); - module.documentation = normalize_doc(&module.documentation); - module.units = normalize_units(module.units.as_ref()); - module.uid = None; - module.ai_state = None; -} - -/// Floating-point tolerance for arc angles. -/// The xmutil path roundtrips angles through a string representation in XMILE XML -/// (C++ float → string → Rust f64), while the native path computes directly. -/// This causes ~1e-13 differences that are inherent to the different computation paths. -const ANGLE_EPSILON: f64 = 1e-10; - -/// Compare two LinkShapes with floating-point tolerance for arc angles. -fn link_shapes_equivalent(a: &view_element::LinkShape, b: &view_element::LinkShape) -> bool { - match (a, b) { - (view_element::LinkShape::Straight, view_element::LinkShape::Straight) => true, - (view_element::LinkShape::Arc(a), view_element::LinkShape::Arc(b)) => { - (a - b).abs() < ANGLE_EPSILON - } - (view_element::LinkShape::MultiPoint(a), view_element::LinkShape::MultiPoint(b)) => a == b, - _ => false, - } -} - -/// Compare two ViewElements with tolerance for floating-point differences. -fn assert_view_elements_equivalent( - xe: &ViewElement, - ne: &ViewElement, - path: &str, - view_idx: usize, - elem_idx: usize, -) { - // For Link elements, use fuzzy comparison for arc angles - if let (ViewElement::Link(xl), ViewElement::Link(nl)) = (xe, ne) { - assert_eq!( - xl.uid, nl.uid, - "{path}: view {view_idx} element {elem_idx} link uid differs" - ); - assert_eq!( - xl.from_uid, nl.from_uid, - "{path}: view {view_idx} element {elem_idx} link from_uid differs" - ); - assert_eq!( - xl.to_uid, nl.to_uid, - "{path}: view {view_idx} element {elem_idx} link to_uid differs" - ); - assert!( - link_shapes_equivalent(&xl.shape, &nl.shape), - "{path}: view {view_idx} element {elem_idx} link shape differs\n xmutil: {:?}\n native: {:?}", - xl.shape, - nl.shape - ); - assert_eq!( - xl.polarity, nl.polarity, - "{path}: view {view_idx} element {elem_idx} link polarity differs" - ); - return; - } - // For all other elements, exact equality - assert_eq!( - xe, ne, - "{path}: view {view_idx} element {elem_idx} differs\n xmutil: {:#?}\n native: {:#?}", - xe, ne - ); -} - -/// Compare two projects for equivalence with detailed error messages. -fn assert_projects_equivalent(xmutil: &Project, native: &Project, path: &str) { - // Compare sim_specs - assert_eq!( - xmutil.sim_specs, native.sim_specs, - "{path}: sim_specs differ\n xmutil: {:?}\n native: {:?}", - xmutil.sim_specs, native.sim_specs - ); - - // Compare dimensions - assert_eq!( - xmutil.dimensions.len(), - native.dimensions.len(), - "{path}: dimension count differs\n xmutil ({} dims): {:?}\n native ({} dims): {:?}", - xmutil.dimensions.len(), - xmutil - .dimensions - .iter() - .map(|d| &d.name) - .collect::>(), - native.dimensions.len(), - native - .dimensions - .iter() - .map(|d| &d.name) - .collect::>() - ); - for (i, (xd, nd)) in xmutil - .dimensions - .iter() - .zip(native.dimensions.iter()) - .enumerate() - { - assert_eq!(xd, nd, "{path}: dimension {i} differs"); - } - - // Compare unit equivalences - assert_eq!( - xmutil.units.len(), - native.units.len(), - "{path}: units count differs\n xmutil ({} units): {:?}\n native ({} units): {:?}", - xmutil.units.len(), - xmutil.units.iter().map(|u| &u.name).collect::>(), - native.units.len(), - native.units.iter().map(|u| &u.name).collect::>() - ); - for (i, (xu, nu)) in xmutil.units.iter().zip(native.units.iter()).enumerate() { - assert_eq!(xu, nu, "{path}: unit {i} ('{}') differs", xu.name); - } - - // Compare models - use name-based matching instead of positional zip - assert_eq!( - xmutil.models.len(), - native.models.len(), - "{path}: model count differs" - ); - - // Build a map of native models by name for matching - let native_models: HashMap<&str, &Model> = - native.models.iter().map(|m| (m.name.as_str(), m)).collect(); - - for xm in &xmutil.models { - let nm = native_models.get(xm.name.as_str()).unwrap_or_else(|| { - panic!( - "{path}: model '{}' found in xmutil but not in native.\n \ - native models: {:?}", - xm.name, - native.models.iter().map(|m| &m.name).collect::>() - ) - }); - assert_model_equivalent(xm, nm, path); - } -} - -/// Compare two models for equivalence. -fn assert_model_equivalent(xm: &Model, nm: &Model, path: &str) { - assert_eq!( - xm.name, nm.name, - "{path}: model name differs: '{}' vs '{}'", - xm.name, nm.name - ); - assert_eq!( - xm.sim_specs, nm.sim_specs, - "{path}: model '{}' sim_specs differ\n xmutil: {:?}\n native: {:?}", - xm.name, xm.sim_specs, nm.sim_specs - ); - - // Compare variable counts with helpful diff output - if xm.variables.len() != nm.variables.len() { - let xmutil_vars: Vec<_> = xm.variables.iter().map(|v| v.get_ident()).collect(); - let native_vars: Vec<_> = nm.variables.iter().map(|v| v.get_ident()).collect(); - - // Find differences - let only_in_xmutil: Vec<_> = xmutil_vars - .iter() - .filter(|v| !native_vars.contains(v)) - .collect(); - let only_in_native: Vec<_> = native_vars - .iter() - .filter(|v| !xmutil_vars.contains(v)) - .collect(); - - panic!( - "{path}: variable count differs ({} vs {})\n \ - only in xmutil: {:?}\n \ - only in native: {:?}", - xm.variables.len(), - nm.variables.len(), - only_in_xmutil, - only_in_native - ); - } - - // Build a map of native variables by identifier for matching - let native_vars: HashMap<&str, &Variable> = - nm.variables.iter().map(|v| (v.get_ident(), v)).collect(); - - // Compare each variable by name (not position) - for xv in &xm.variables { - let nv = native_vars.get(xv.get_ident()).unwrap_or_else(|| { - panic!( - "{path}: variable '{}' found in xmutil but not in native", - xv.get_ident() - ) - }); - if xv != *nv { - panic!( - "{path}: variable '{}' differs\n xmutil: {:#?}\n native: {:#?}", - xv.get_ident(), - xv, - nv - ); - } - } - - // Compare views - assert_eq!( - xm.views.len(), - nm.views.len(), - "{path}: view count differs ({} vs {})", - xm.views.len(), - nm.views.len() - ); - for (i, (xv, nv)) in xm.views.iter().zip(nm.views.iter()).enumerate() { - let View::StockFlow(x_sf) = xv; - let View::StockFlow(n_sf) = nv; - assert_eq!( - x_sf.elements.len(), - n_sf.elements.len(), - "{path}: view {i} element count differs ({} vs {})\n xmutil: {}\n native: {}", - x_sf.elements.len(), - n_sf.elements.len(), - summarize_view_elements(&xm.views), - summarize_view_elements(&nm.views), - ); - for (j, (xe, ne)) in x_sf.elements.iter().zip(n_sf.elements.iter()).enumerate() { - assert_view_elements_equivalent(xe, ne, path, i, j); - } - } -} - -/// Test a single MDL file for equivalence between parsers. -fn test_single_mdl(mdl_path: &str) { - let full_path = format!("../../{mdl_path}"); - eprintln!("testing: {mdl_path}"); - - let content = - fs::read_to_string(&full_path).unwrap_or_else(|e| panic!("Failed to read {mdl_path}: {e}")); - - // Parse via xmutil (MDL -> XMILE -> datamodel) - let xmutil_project = - open_vensim_xmutil(&content).unwrap_or_else(|e| panic!("{mdl_path}: xmutil failed: {e}")); - - // Parse via native (MDL -> datamodel directly) - let native_project = - open_vensim(&content).unwrap_or_else(|e| panic!("{mdl_path}: native failed: {e}")); - - // Normalize both projects - let xmutil_norm = normalize_project(xmutil_project); - let native_norm = normalize_project(native_project); - - // Compare - assert_projects_equivalent(&xmutil_norm, &native_norm, mdl_path); -} - -#[test] -fn test_mdl_equivalence() { - for &mdl_path in EQUIVALENT_MODELS { - test_single_mdl(mdl_path); - } -} - -/// Standalone test for the C-LEARN model which causes a segfault in xmutil's C++ code. -#[test] -fn test_clearn_xmutil() { - let mdl_path = "test/xmutil_test_models/C-LEARN v77 for Vensim.mdl"; - let full_path = format!("../../{mdl_path}"); - - let content = - fs::read_to_string(&full_path).unwrap_or_else(|e| panic!("Failed to read {mdl_path}: {e}")); - - // Parse via xmutil (MDL -> XMILE -> datamodel) -- this segfaults in xmutil's C++ code - let _project = - open_vensim_xmutil(&content).unwrap_or_else(|e| panic!("{mdl_path}: xmutil failed: {e}")); -} - -/// Verify the native Rust parser can handle the C-LEARN model (no xmutil dependency). -#[test] -fn test_clearn_native() { - let mdl_path = "test/xmutil_test_models/C-LEARN v77 for Vensim.mdl"; - let full_path = format!("../../{mdl_path}"); - - let content = - fs::read_to_string(&full_path).unwrap_or_else(|e| panic!("Failed to read {mdl_path}: {e}")); - - // Parse via native (MDL -> datamodel directly) - let _project = - open_vensim(&content).unwrap_or_else(|e| panic!("{mdl_path}: native failed: {e}")); -} - -// ===== Non-panicking comparison infrastructure ===== -// -// These functions collect all differences into a Vec instead of -// panicking on the first mismatch. This lets a single test run surface -// every class of problem at once. - -/// Describes one field-level difference between the xmutil and native output. -struct Diff { - /// Dot-path to the differing field, e.g. "model[main].var[population].equation" - path: String, - detail: String, -} - -impl std::fmt::Display for Diff { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, " {}: {}", self.path, self.detail) - } -} - -/// Collect all differences between two normalized projects. -fn collect_project_diffs(xmutil: &Project, native: &Project, path: &str) -> Vec { - let mut diffs = Vec::new(); - - // sim_specs - if xmutil.sim_specs != native.sim_specs { - diffs.push(Diff { - path: format!("{path}.sim_specs"), - detail: format!( - "xmutil: {:?}\nnative: {:?}", - xmutil.sim_specs, native.sim_specs - ), - }); - } - - // dimensions - collect_dimension_diffs(&mut diffs, xmutil, native, path); - - // unit equivalences - collect_unit_diffs(&mut diffs, xmutil, native, path); - - // models - collect_model_diffs(&mut diffs, xmutil, native, path); - - diffs -} - -fn collect_dimension_diffs(diffs: &mut Vec, xmutil: &Project, native: &Project, path: &str) { - let x_by_name: HashMap<&str, &Dimension> = xmutil - .dimensions - .iter() - .map(|d| (d.name.as_str(), d)) - .collect(); - let n_by_name: HashMap<&str, &Dimension> = native - .dimensions - .iter() - .map(|d| (d.name.as_str(), d)) - .collect(); - - for (name, xd) in &x_by_name { - match n_by_name.get(name) { - None => diffs.push(Diff { - path: format!("{path}.dim[{name}]"), - detail: "only in xmutil".into(), - }), - Some(nd) if *xd != *nd => diffs.push(Diff { - path: format!("{path}.dim[{name}]"), - detail: format!("xmutil: {:?}\nnative: {:?}", xd, nd), - }), - _ => {} - } - } - for name in n_by_name.keys() { - if !x_by_name.contains_key(name) { - diffs.push(Diff { - path: format!("{path}.dim[{name}]"), - detail: "only in native".into(), - }); - } - } -} - -fn collect_unit_diffs(diffs: &mut Vec, xmutil: &Project, native: &Project, path: &str) { - let x_by_name: HashMap<&str, _> = xmutil.units.iter().map(|u| (u.name.as_str(), u)).collect(); - let n_by_name: HashMap<&str, _> = native.units.iter().map(|u| (u.name.as_str(), u)).collect(); - - for (name, xu) in &x_by_name { - match n_by_name.get(name) { - None => diffs.push(Diff { - path: format!("{path}.unit[{name}]"), - detail: "only in xmutil".into(), - }), - Some(nu) if *xu != *nu => diffs.push(Diff { - path: format!("{path}.unit[{name}]"), - detail: format!("xmutil: {:?}\nnative: {:?}", xu, nu), - }), - _ => {} - } - } - for name in n_by_name.keys() { - if !x_by_name.contains_key(name) { - diffs.push(Diff { - path: format!("{path}.unit[{name}]"), - detail: "only in native".into(), - }); - } - } -} - -fn collect_model_diffs(diffs: &mut Vec, xmutil: &Project, native: &Project, path: &str) { - let x_by_name: HashMap<&str, &Model> = - xmutil.models.iter().map(|m| (m.name.as_str(), m)).collect(); - let n_by_name: HashMap<&str, &Model> = - native.models.iter().map(|m| (m.name.as_str(), m)).collect(); - - for (name, xm) in &x_by_name { - match n_by_name.get(name) { - None => diffs.push(Diff { - path: format!("{path}.model[{name}]"), - detail: "only in xmutil".into(), - }), - Some(nm) => collect_single_model_diffs(diffs, xm, nm, &format!("{path}.model[{name}]")), - } - } - for name in n_by_name.keys() { - if !x_by_name.contains_key(name) { - diffs.push(Diff { - path: format!("{path}.model[{name}]"), - detail: "only in native".into(), - }); - } - } -} - -fn collect_single_model_diffs(diffs: &mut Vec, xm: &Model, nm: &Model, path: &str) { - diff_field(diffs, path, "sim_specs", &xm.sim_specs, &nm.sim_specs); - - let x_by_ident: HashMap<&str, &Variable> = - xm.variables.iter().map(|v| (v.get_ident(), v)).collect(); - let n_by_ident: HashMap<&str, &Variable> = - nm.variables.iter().map(|v| (v.get_ident(), v)).collect(); - - // Variables only in xmutil - let mut only_xmutil: Vec<&str> = x_by_ident - .keys() - .filter(|k| !n_by_ident.contains_key(*k)) - .copied() - .collect(); - only_xmutil.sort(); - for ident in &only_xmutil { - diffs.push(Diff { - path: format!("{path}.var[{ident}]"), - detail: "only in xmutil".into(), - }); - } - - // Variables only in native - let mut only_native: Vec<&str> = n_by_ident - .keys() - .filter(|k| !x_by_ident.contains_key(*k)) - .copied() - .collect(); - only_native.sort(); - for ident in &only_native { - diffs.push(Diff { - path: format!("{path}.var[{ident}]"), - detail: "only in native".into(), - }); - } - - // Variables present in both but differing - let mut common: Vec<&str> = x_by_ident - .keys() - .filter(|k| n_by_ident.contains_key(*k)) - .copied() - .collect(); - common.sort(); - for ident in common { - let xv = x_by_ident[ident]; - let nv = n_by_ident[ident]; - if xv != nv { - collect_variable_field_diffs(diffs, xv, nv, &format!("{path}.var[{ident}]")); - } - } -} - -struct FlowAuxFields { - equation: Equation, - documentation: String, - units: Option, - gf: Option, - non_negative: bool, -} - -/// Extract the comparable fields from a Flow or Aux variable. -fn extract_flow_aux_fields(v: &Variable) -> Option { - match v { - Variable::Flow(f) => Some(FlowAuxFields { - equation: f.equation.clone(), - documentation: f.documentation.clone(), - units: f.units.clone(), - gf: f.gf.clone(), - non_negative: f.compat.non_negative, - }), - Variable::Aux(a) => Some(FlowAuxFields { - equation: a.equation.clone(), - documentation: a.documentation.clone(), - units: a.units.clone(), - gf: a.gf.clone(), - non_negative: a.compat.non_negative, - }), - _ => None, - } -} - -/// Compare individual fields of two variables to pinpoint exactly what differs. -fn collect_variable_field_diffs(diffs: &mut Vec, xv: &Variable, nv: &Variable, path: &str) { - // Type mismatch (Stock vs Module are always significant; - // Flow/Aux mismatches are tolerated because the two parsers may - // disagree on net-flow synthesis, leaving a variable as Aux on one - // side and Flow on the other). - let is_stock_or_module = |v: &Variable| matches!(v, Variable::Stock(_) | Variable::Module(_)); - if is_stock_or_module(xv) != is_stock_or_module(nv) { - let xtype = match xv { - Variable::Stock(_) => "Stock", - Variable::Flow(_) => "Flow", - Variable::Aux(_) => "Aux", - Variable::Module(_) => "Module", - }; - let ntype = match nv { - Variable::Stock(_) => "Stock", - Variable::Flow(_) => "Flow", - Variable::Aux(_) => "Aux", - Variable::Module(_) => "Module", - }; - diffs.push(Diff { - path: format!("{path}.type"), - detail: format!("xmutil: {xtype}, native: {ntype}"), - }); - return; - } - - // Compare fields depending on type - match (xv, nv) { - (Variable::Stock(xs), Variable::Stock(ns)) => { - diff_field(diffs, path, "equation", &xs.equation, &ns.equation); - diff_field( - diffs, - path, - "documentation", - &xs.documentation, - &ns.documentation, - ); - diff_field(diffs, path, "units", &xs.units, &ns.units); - diff_field(diffs, path, "inflows", &xs.inflows, &ns.inflows); - diff_field(diffs, path, "outflows", &xs.outflows, &ns.outflows); - diff_field( - diffs, - path, - "non_negative", - &xs.compat.non_negative, - &ns.compat.non_negative, - ); - } - (Variable::Module(xmod), Variable::Module(nmod)) => { - diff_field( - diffs, - path, - "documentation", - &xmod.documentation, - &nmod.documentation, - ); - diff_field(diffs, path, "units", &xmod.units, &nmod.units); - diff_field( - diffs, - path, - "references", - &xmod.references, - &nmod.references, - ); - } - _ => { - // Flow/Aux cross-comparison (or same-type Flow/Flow, Aux/Aux). - // Extract the common fields from whichever variant we have. - let x_fields = extract_flow_aux_fields(xv); - let n_fields = extract_flow_aux_fields(nv); - if let (Some(xf), Some(nf)) = (x_fields, n_fields) { - diff_field(diffs, path, "equation", &xf.equation, &nf.equation); - diff_field( - diffs, - path, - "documentation", - xf.documentation.as_str(), - nf.documentation.as_str(), - ); - diff_field(diffs, path, "units", &xf.units, &nf.units); - diff_field(diffs, path, "gf", &xf.gf, &nf.gf); - diff_field( - diffs, - path, - "non_negative", - &xf.non_negative, - &nf.non_negative, - ); - } - } - } -} - -fn diff_field( - diffs: &mut Vec, - path: &str, - field: &str, - xval: &T, - nval: &T, -) { - if xval != nval { - diffs.push(Diff { - path: format!("{path}.{field}"), - detail: format!("xmutil: {:?}\nnative: {:?}", xval, nval), - }); - } -} - -/// Compare the xmutil and native parser outputs for the C-LEARN model, -/// collecting all differences rather than stopping at the first. -/// -/// Ignored: 5 remaining diffs (down from 233 at the start of MDL full-compat work). -/// Remaining issues: -/// - 2 dimension mapping diffs (semi_agg, aggregated_regions): xmutil emits empty -/// element_map for multi-target mappings, but the native parser builds the full -/// per-element correspondence. -/// - 3 equation diffs: xmutil emits ramp_from_to() (a macro call) but the native -/// parser inlines it as ramp() (the expanded form). Resolving requires macro -/// expansion support in the native parser's post-processing stage. -#[test] -#[ignore] -fn test_clearn_equivalence() { - let mdl_path = "test/xmutil_test_models/C-LEARN v77 for Vensim.mdl"; - let full_path = format!("../../{mdl_path}"); - - let content = - fs::read_to_string(&full_path).unwrap_or_else(|e| panic!("Failed to read {mdl_path}: {e}")); - - let xmutil_project = - open_vensim_xmutil(&content).unwrap_or_else(|e| panic!("{mdl_path}: xmutil failed: {e}")); - - let native_project = - open_vensim(&content).unwrap_or_else(|e| panic!("{mdl_path}: native failed: {e}")); - - let xmutil_norm = normalize_project(xmutil_project); - let native_norm = normalize_project(native_project); - - let diffs = collect_project_diffs(&xmutil_norm, &native_norm, mdl_path); - - if !diffs.is_empty() { - let mut report = format!("\n{} equivalence differences found:\n", diffs.len()); - for d in &diffs { - report.push_str(&format!("{d}\n")); - } - panic!("{report}"); - } -} - -#[test] -fn test_normalize_equation_sorts_arrayed_elements() { - // Arrayed equations with identical elements in different orders should be - // equal after normalization. This verifies that the element sort in - // normalize_equation works correctly. - let mut eq_a = Equation::Arrayed( - vec!["dim".to_string()], - vec![ - ("b".to_string(), "1".to_string(), None, None), - ("a".to_string(), "2".to_string(), None, None), - ("c".to_string(), "3".to_string(), None, None), - ], - None, - false, - ); - let mut eq_b = Equation::Arrayed( - vec!["dim".to_string()], - vec![ - ("a".to_string(), "2".to_string(), None, None), - ("c".to_string(), "3".to_string(), None, None), - ("b".to_string(), "1".to_string(), None, None), - ], - None, - false, - ); - - normalize_equation(&mut eq_a); - normalize_equation(&mut eq_b); - - assert_eq!( - eq_a, eq_b, - "Arrayed equations with different element order should be equal after normalization" - ); -} - -#[test] -fn test_normalize_equation_sorts_multidim_arrayed_elements() { - // Multi-dimensional arrayed equations with comma-separated subscript keys - // should also sort correctly. - let mut eq_a = Equation::Arrayed( - vec!["dima".to_string(), "dimb".to_string()], - vec![ - ("b,y".to_string(), "3".to_string(), None, None), - ("a,x".to_string(), "1".to_string(), None, None), - ("a,y".to_string(), "2".to_string(), None, None), - ("b,x".to_string(), "4".to_string(), None, None), - ], - None, - false, - ); - let mut eq_b = Equation::Arrayed( - vec!["dima".to_string(), "dimb".to_string()], - vec![ - ("a,x".to_string(), "1".to_string(), None, None), - ("a,y".to_string(), "2".to_string(), None, None), - ("b,x".to_string(), "4".to_string(), None, None), - ("b,y".to_string(), "3".to_string(), None, None), - ], - None, - false, - ); - - normalize_equation(&mut eq_a); - normalize_equation(&mut eq_b); - - assert_eq!( - eq_a, eq_b, - "Multi-dim arrayed equations should be equal after normalization" - ); -} diff --git a/src/simlin-engine/tests/integration/mdl_roundtrip.rs b/src/simlin-engine/tests/integration/mdl_roundtrip.rs index 955e982c7..e6ccc1e8c 100644 --- a/src/simlin-engine/tests/integration/mdl_roundtrip.rs +++ b/src/simlin-engine/tests/integration/mdl_roundtrip.rs @@ -768,7 +768,7 @@ fn view_element_roundtrip() { // --------------------------------------------------------------------------- #[test] -#[ignore] +#[ignore = "an instrument: writes four saved models to the temp directory for a person to open in Vensim; run under the gates profile"] fn write_mdl_for_vensim_validation() { let output_dir = std::env::temp_dir().join("simlin-mdl-roundtrip"); fs::create_dir_all(&output_dir).expect("create output dir"); @@ -2040,10 +2040,10 @@ fn writer_output_idempotence_ratchet() { } /// The corpus ratchets skip C-LEARN (1.4 MB) to fit a debug build's budget; -/// this gate holds it to the same fixed point in a release run -/// (`cargo test --release -- --ignored`). +/// this gate holds it to the same fixed point +/// (`scripts/gates.sh clearn_save_is_a_fixed_point`). #[test] -#[ignore] +#[ignore = "C-LEARN saved as MDL twice; run under the gates profile"] fn clearn_save_is_a_fixed_point() { let path = resolve_path("test/xmutil_test_models/C-LEARN v77 for Vensim.mdl"); let source = fs::read_to_string(&path).expect("C-LEARN reads"); @@ -2058,12 +2058,12 @@ fn clearn_save_is_a_fixed_point() { } /// C-LEARN's MDL save simulates as C-LEARN does, series for series and bit -/// for bit, in a release run (`cargo test --release -- --ignored`). The save +/// for bit (`scripts/gates.sh clearn_save_simulates_as_clearn_does`). The save /// used to lose its regional mappings' element maps (`-> (COP: ..., COP /// Remaining Developing)`, one element to a subrange) and the initial values /// of four arrayed ACTIVE INITIAL variables, so it did not compile. #[test] -#[ignore] +#[ignore = "C-LEARN and its MDL save both compiled and run; run under the gates profile"] fn clearn_save_simulates_as_clearn_does() { use simlin_engine::db::{ LtmOverlay, SimlinDb, compile_project_incremental, sync_from_datamodel_incremental, diff --git a/src/simlin-engine/tests/integration/simulate.rs b/src/simlin-engine/tests/integration/simulate.rs index 48de228b4..02b9eca0b 100644 --- a/src/simlin-engine/tests/integration/simulate.rs +++ b/src/simlin-engine/tests/integration/simulate.rs @@ -2730,14 +2730,6 @@ TIME STEP = 1 ~~| } } -// Commented out: test_generator approach is useful for discovery but generates many tests. -// Use the corpus_tests! invocation below for the curated list. -// #[test_generator::test_resources("test/sdeverywhere/models/**/*.xmile")] -// fn simulates_sdeverywhere(resource: &str) { -// let resource = format!("../../{}", resource); -// simulate_path(&resource); -// } - // SDEverywhere test models from test/sdeverywhere/models/**/*.xmile // These are Vensim models converted to XMILE format. corpus_tests! { @@ -3907,8 +3899,7 @@ fn simulates_wrld3_03_wasm() { /// NaN-vs-`:NA:` / boundary); none is "unknown". /// /// This set is GUARDED for exactness by the committed `clearn_residual_exactness` -/// regression test (run it to re-derive/verify after an engine change: -/// `cargo test --release -- --ignored clearn_residual_exactness`). That test +/// regression test, which runs in the default suite. That test /// re-runs C-LEARN through the same `classify_vdf_ident` comparator with NO /// exclusion and asserts the live failing set equals this list, failing loudly /// (with the symmetric difference) if the residual grew (a regression) or shrank @@ -3979,14 +3970,10 @@ const EXPECTED_VDF_RESIDUAL: &[&str] = &[ "last_set_target_year", // INIT(VECTOR SELECT(...)); every cell is -2*NA (vdf -1.298e33 vs sim -6.49e32 sentinel). ]; -// FULL end-to-end C-LEARN simulation against `Ref.vdf`. Un-stubbed (no longer a -// permanently-skipped placeholder): C-LEARN compiles via the incremental path, -// runs to FINAL TIME, and matches `Ref.vdf` within the 1% cross-simulator -// tolerance on every matched, non-excluded ident (~2924 of ~2951 matched, after -// the pinned 434-column reference-missing skip). Kept `#[test] #[ignore]` -// purely for RUNTIME CLASS (C-LEARN is ~53k lines / 1.4 MB, ~5s just to parse on -// release), so the capped default `cargo test` set stays under the 3-minute cap; -// run it explicitly via `--ignored` (AC8.3). +// FULL end-to-end C-LEARN simulation against `Ref.vdf`: C-LEARN compiles via +// the incremental path, runs to FINAL TIME, and matches `Ref.vdf` within the 1% +// cross-simulator tolerance on every matched, non-excluded ident (~2924 of +// ~2951 matched, after the pinned 434-column reference-missing skip). // // History (the formerly-listed blockers are CLEARED on this branch): // * C-LEARN's four macros (SAMPLE UNTIL, SSHAPE, RAMP FROM TO, INIT) parse, @@ -4107,9 +4094,9 @@ fn run_clearn_vs_vdf() -> (Results, Results) { /// checked against the VDF oracle -- so the resumable cursor is proven on a real /// model at C-LEARN scale (wasm-backend.AC2.3 segmented-run parity). /// -/// Run with: cargo test --release -- --ignored simulates_clearn_wasm +/// Run with: scripts/gates.sh simulates_clearn_wasm #[test] -#[ignore] +#[ignore = "C-LEARN compiled to wasm and run under the interpreter, whole and in two segments; run under the gates profile"] fn simulates_clearn_wasm() { let datamodel_project = clearn_datamodel(); @@ -7244,9 +7231,9 @@ fn clearn_ltm_var_count_guardrail() { /// /// C-LEARN had 50 such failures before #912/#913; the remaining 28 /// partial-equation warnings are the separately-tracked non-parse kinds. -// Run with: cargo test --release -- --ignored clearn_ltm_partials_all_parse +// Run with: scripts/gates.sh clearn_ltm_partials_all_parse #[test] -#[ignore] +#[ignore = "C-LEARN's diagnostics under LTM, every generated partial; run under the gates profile"] fn clearn_ltm_partials_all_parse() { use simlin_engine::db::collect_all_diagnostics; @@ -7277,13 +7264,13 @@ fn clearn_ltm_partials_all_parse() { ); } -// Still `#[ignore]`d, but no longer for time: `simulates_clearn` now runs by -// default and checks C-LEARN's numbers against `Ref.vdf`, which subsumes -// "compiles and runs". Kept as the structural-only probe to reach for when the -// numeric gate fails and the question is whether compilation itself broke. -// Run with: cargo test -- --ignored compiles_and_runs_clearn_structural +// `simulates_clearn` runs by default and checks C-LEARN's numbers against +// `Ref.vdf`, which subsumes "compiles and runs". This is the structural-only +// check to reach for when the numeric gate fails and the question is whether +// compilation itself broke. +// Run with: scripts/gates.sh compiles_and_runs_clearn_structural #[test] -#[ignore] +#[ignore = "C-LEARN compiled and run with no numeric comparison, a second C-LEARN compile the default suite does not need; run under the gates profile"] fn compiles_and_runs_clearn_structural() { use simlin_engine::common::ErrorCode; diff --git a/src/simlin-engine/tests/integration/simulate_ltm.rs b/src/simlin-engine/tests/integration/simulate_ltm.rs index b15adf016..083a2cec3 100644 --- a/src/simlin-engine/tests/integration/simulate_ltm.rs +++ b/src/simlin-engine/tests/integration/simulate_ltm.rs @@ -9545,10 +9545,10 @@ fn discovery_recovers_cross_agg_loops_matches_exhaustive() { /// instrumentation corrupted the simulation itself. /// /// `#[ignore]`d for runtime only (C-LEARN is ~53k lines / 1.4 MB and the LTM -/// compile is heavy); run explicitly with: -/// cargo test --release --test integration -- --ignored clearn_with_ltm +/// compile is heavy); run with: +/// scripts/gates.sh clearn_with_ltm_simulates_model_vars_identically #[test] -#[ignore] +#[ignore = "C-LEARN compiled and run with and without LTM, every model series compared; run under the gates profile"] fn clearn_with_ltm_simulates_model_vars_identically() { let mdl_path = "../../test/xmutil_test_models/C-LEARN v77 for Vensim.mdl"; let contents = std::fs::read_to_string(mdl_path) @@ -11695,7 +11695,7 @@ fn test_whole_rhs_mapped_reducer_routes_through_synthetic_agg() { /// cargo test -p simlin-engine --release --test integration -- --ignored \ /// clearn_ltm_slot_maxima_digest #[test] -#[ignore] +#[ignore = "C-LEARN compiled and run under LTM, every LTM slot read; run under the gates profile"] fn clearn_ltm_slot_maxima_digest() { use simlin_engine::common::CanonicalDimensionName; use simlin_engine::db::project_dimensions_context; diff --git a/src/simlin-engine/tests/integration/simulate_ltm_pinned.rs b/src/simlin-engine/tests/integration/simulate_ltm_pinned.rs index 735c04abd..d8f80acfe 100644 --- a/src/simlin-engine/tests/integration/simulate_ltm_pinned.rs +++ b/src/simlin-engine/tests/integration/simulate_ltm_pinned.rs @@ -1199,13 +1199,13 @@ fn pinned_scalar_feeder_agg_loop_dedups_in_exhaustive_mode() { /// loop score over `scenario` whose deterministic-scenario slot is finite, /// eventually non-zero, and negative (it is a balancing loop). /// -/// `#[ignore]`d for runtime class only: C-LEARN's debug-mode parse + -/// LTM-discovery compile measures ~40s against the 3-minute +/// `#[ignore]`d for runtime only: C-LEARN's debug-mode parse + +/// LTM-discovery compile measures ~35s against the 3-minute /// `cargo test --workspace` budget (see the design plan's "Additional -/// Considerations"). Run explicitly with: -/// cargo test --release -p simlin-engine --test integration -- --ignored clearn +/// Considerations"). Run with: +/// scripts/gates.sh clearn_pinned_climate_loop_is_scored #[test] -#[ignore] +#[ignore = "C-LEARN compiled and run under LTM with a pinned loop; run under the gates profile"] fn clearn_pinned_climate_loop_is_scored() { use simlin_engine::{ModelOperation, ModelPatch, ProjectPatch, apply_patch}; diff --git a/src/simlin-engine/tests/integration/simulate_ltm_wasm.rs b/src/simlin-engine/tests/integration/simulate_ltm_wasm.rs index e763b0440..6791689b4 100644 --- a/src/simlin-engine/tests/integration/simulate_ltm_wasm.rs +++ b/src/simlin-engine/tests/integration/simulate_ltm_wasm.rs @@ -731,16 +731,15 @@ fn discovery_arms_race_matches_vm() { /// VM-side peer `clearn_ltm_discovery_compiles` already runs at /// `#[ignore]`); running it counts against the 3-minute /// `cargo test --workspace` cap if left in the default suite. Run -/// explicitly with: -/// cargo test --release -p simlin-engine --test integration \ -/// -- --ignored --nocapture discovery_clearn_matches_vm_wasm +/// with: +/// scripts/gates.sh --nocapture discovery_clearn_matches_vm_wasm /// /// The model lives outside the XMILE loader's `test/` root (it is a /// Vensim MDL under `test/xmutil_test_models/`), so it is loaded via /// `open_vensim` directly rather than through this binary's /// XMILE-only `load` helper. #[test] -#[ignore = "wasm compile of 1.4 MB MDL is slow; run with `cargo test --release -- --ignored`"] +#[ignore = "C-LEARN under LTM discovery on the VM and on wasm under the interpreter; run under the gates profile"] fn discovery_clearn_matches_vm_wasm() { let path = "../../test/xmutil_test_models/C-LEARN v77 for Vensim.mdl"; let mdl = @@ -752,20 +751,15 @@ fn discovery_clearn_matches_vm_wasm() { /// AC2.5 (heavy ignored twin): World3-03 discovery parity. /// -/// `#[ignore]`d because (a) wasm-compiling a 166-variable Vensim -/// model is slow, and (b) the VM-side discovery on World3 currently -/// does not terminate -- see `world3_discovery_single_timestep` for -/// the documented Finding 3 (GH #540) and the surrounding RSS/time- -/// budget bound. Until #540 is fixed the VM run alone exhausts the -/// budget, so this twin cannot complete; once #540 is fixed the -/// twin's VM and wasm runs both terminate and this test asserts they -/// agree on the discovered loop set and per-loop score series. +/// `#[ignore]`d for runtime only: World3 runs under LTM discovery on +/// the VM and again on wasm under the interpreter, about 20 s on a +/// debug build. The two agree on the discovered loop set and on each +/// loop's score series. /// -/// Run explicitly with: -/// cargo test --release -p simlin-engine --test integration \ -/// -- --ignored --nocapture discovery_world3_matches_vm_wasm +/// Run with: +/// scripts/gates.sh --nocapture discovery_world3_matches_vm_wasm #[test] -#[ignore = "wasm compile of 166-var World3 is slow; VM discovery is also blocked on #540"] +#[ignore = "World3 under LTM discovery on the VM and on wasm under the interpreter; run under the gates profile"] fn discovery_world3_matches_vm_wasm() { let path = "../../test/metasd/WRLD3-03/wrld3-03.mdl"; let mdl = diff --git a/src/simlin-engine/tests/integration/wrld3_ltm_panic.rs b/src/simlin-engine/tests/integration/wrld3_ltm_panic.rs index b697957a2..e3627e5c7 100644 --- a/src/simlin-engine/tests/integration/wrld3_ltm_panic.rs +++ b/src/simlin-engine/tests/integration/wrld3_ltm_panic.rs @@ -102,10 +102,9 @@ fn wrld3_ltm_compilation_finishes_in_time() { /// every-push compilation path under its own 60s thread budget. Run this /// one on demand when changing enumeration logic: /// -/// cargo test --release -p simlin-engine --test integration \ -/// -- --ignored wrld3_element_level_enumeration_is_uncapped +/// scripts/gates.sh wrld3_element_level_enumeration_is_uncapped #[test] -#[ignore] +#[ignore = "Johnson's algorithm over World3's 166-node SCC; run under the gates profile"] fn wrld3_element_level_enumeration_is_uncapped() { let project = load_wrld3(); let mut db = SimlinDb::default(); diff --git a/src/simlin-mcp/src/main.rs b/src/simlin-mcp/src/main.rs index d301b865e..8b299146a 100644 --- a/src/simlin-mcp/src/main.rs +++ b/src/simlin-mcp/src/main.rs @@ -129,43 +129,95 @@ mod tests { ); } + /// What the tag guard does with what `git tag --list` printed. + #[derive(Debug, PartialEq)] + enum TagCheck<'a> { + /// Compare `pysimlin.version` with this version, the newest tag's. + Compare(&'a str), + /// No tags in a clone that is not CI's: nothing to compare against. + Skip, + /// No tags where there must be some, or a tag of an unexpected form. + Fail(String), + } + + /// Decide the tag guard from the tag listing (newest first, one per + /// line; `None` when git itself failed) and whether this is a CI run. + /// + /// A clone without the tags (a developer's fresh shallow clone) skips. CI + /// does not get to: its Build job fetches the tags by name + /// (`.github/workflows/ci.yaml`), and a guard that skipped there whenever + /// that fetch brought nothing would pass on every run while checking + /// nothing. + fn tag_check(tags: Option<&str>, in_ci: bool) -> TagCheck<'_> { + let newest = tags.and_then(|tags| tags.lines().map(str::trim).find(|l| !l.is_empty())); + match newest { + Some(tag) => match tag.strip_prefix("pysimlin-v") { + Some(version) => TagCheck::Compare(version), + None => TagCheck::Fail(format!("unexpected tag format: {tag}")), + }, + None if in_ci => TagCheck::Fail( + "no pysimlin-v* tags are visible in a CI checkout: the job that runs this \ + test must fetch them (`git fetch origin \ + 'refs/tags/pysimlin-v*:refs/tags/pysimlin-v*'`)" + .to_string(), + ), + None => TagCheck::Skip, + } + } + + #[test] + fn the_tag_guard_skips_only_outside_ci() { + let newest_first = "pysimlin-v0.8.5\npysimlin-v0.8.4\n"; + for in_ci in [false, true] { + assert_eq!( + tag_check(Some(newest_first), in_ci), + TagCheck::Compare("0.8.5") + ); + assert!(matches!( + tag_check(Some("v0.8.5\n"), in_ci), + TagCheck::Fail(_) + )); + } + // No tags, whether git listed none or could not run. + for tags in [Some(""), Some("\n"), None] { + assert_eq!(tag_check(tags, false), TagCheck::Skip); + assert!(matches!(tag_check(tags, true), TagCheck::Fail(_))); + } + } + // version-mgmt.AC1.7: pysimlin.version matches latest pysimlin git tag. // // pysimlin's version has no in-tree source of truth to compare against -- // setuptools-scm derives it from the `pysimlin-v*` tag itself (see // `tag_regex` in src/pysimlin/pyproject.toml) -- so the tag is the only // thing this can be checked against, and the check needs the tags to be - // present locally. CI's Build-job checkout sets `fetch-tags: true` - // specifically so this guard runs there. A clone without the tags (e.g. a - // developer's fresh shallow clone) skips rather than fails -- announced - // rather than silent, so a run that unexpectedly finds no tags is - // attributable. + // present locally. `tag_check` says what happens when they are not. #[test] fn pysimlin_version_matches_latest_tag() { let output = std::process::Command::new("git") .args(["tag", "--list", "pysimlin-v*", "--sort=-v:refname"]) .output() .expect("git tag command failed"); - let tags = String::from_utf8(output.stdout).unwrap(); - if !output.status.success() || tags.trim().is_empty() { - eprintln!( + let tags = output + .status + .success() + .then(|| String::from_utf8_lossy(&output.stdout).into_owned()); + // The runners whose workflow fetches the tags; GitHub Actions sets + // this for every step. + let in_ci = std::env::var_os("GITHUB_ACTIONS").is_some(); + match tag_check(tags.as_deref(), in_ci) { + TagCheck::Compare(version) => assert_eq!( + env!("PYSIMLIN_VERSION"), + version, + "pysimlin.version is stale (contains {}, latest tag is {version})", + env!("PYSIMLIN_VERSION"), + ), + TagCheck::Skip => eprintln!( "SKIPPING pysimlin_version_matches_latest_tag: no pysimlin-v* tags are \ - visible. CI fetches tags (fetch-tags: true in ci.yaml), so this skip is \ - expected only on a local clone without them; run `git fetch --tags` to \ - exercise this guard." - ); - return; + visible in this clone; run `git fetch --tags` to exercise this guard." + ), + TagCheck::Fail(reason) => panic!("{reason}"), } - let latest_tag = tags.lines().next().expect("no pysimlin tags found"); - let version = latest_tag - .strip_prefix("pysimlin-v") - .expect("unexpected tag format"); - assert_eq!( - env!("PYSIMLIN_VERSION"), - version, - "pysimlin.version is stale (contains {}, latest tag is {version})", - env!("PYSIMLIN_VERSION"), - ); } // version-mgmt.AC1.8: compiled content contains the substituted version diff --git a/src/simlin-serve/src/parse.rs b/src/simlin-serve/src/parse.rs index ccb973dab..e7d687cc0 100644 --- a/src/simlin-serve/src/parse.rs +++ b/src/simlin-serve/src/parse.rs @@ -10,9 +10,8 @@ //! now the surface is minimal: one dispatcher per `ProjectFormat`. The //! reverse direction (project -> canonical JSON) belongs to //! `ProjectDoc::current_state_as_json_string`, which every read path goes -//! through so the doc stays the source of truth. Note: `.mdl` is parsed -//! via the native Rust parser (`open_vensim`), not the xmutil C++ path — -//! see Phase 1 note 4 in the implementation plan. +//! through so the doc stays the source of truth. `.mdl` is parsed by the +//! engine's native Rust parser (`open_vensim`). use std::io::Cursor; diff --git a/src/simlin-serve/tests/integration/smoke.rs b/src/simlin-serve/tests/integration/smoke.rs index 761c50b43..cea141371 100644 --- a/src/simlin-serve/tests/integration/smoke.rs +++ b/src/simlin-serve/tests/integration/smoke.rs @@ -375,7 +375,7 @@ fn dump_buffer(label: &str, buf: &Mutex>) { } #[tokio::test] -#[ignore] +#[ignore = "spawns the simlin-serve binary with its embedded SPA; CI's serve-smoke job runs it"] async fn smoke_end_to_end_browser_and_mcp_paths() { // ---- 1. Tempdir + fixtures ---- let temp = TempDir::new().expect("tempdir"); diff --git a/src/xmutil/Cargo.toml b/src/xmutil/Cargo.toml deleted file mode 100644 index d7e2f332d..000000000 --- a/src/xmutil/Cargo.toml +++ /dev/null @@ -1,18 +0,0 @@ -[package] -name = "xmutil" -version = "1.1.0" -description = "convert Vensim MDL files to XMILE" -authors = ["Bobby Powers "] -homepage = "https://github.com/bpowers/xmutil-rs" -repository = "https://github.com/bpowers/xmutil-rs" -readme = "README.md" -license = "Apache-2.0" -edition = "2024" -build = "build.rs" - -[dependencies] - -[build-dependencies] -cc = "1" - -[dev-dependencies] diff --git a/src/xmutil/README.md b/src/xmutil/README.md deleted file mode 100644 index 85519c5fa..000000000 --- a/src/xmutil/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# xmutil-rs: convert Vensim mdl files to XMILE - -This project wraps [bobeberlein/xmutil](https://github.com/bobeberlein/xmutil) as a Rust library, enabling Rust programs to perform one-way conversion of Vensim `.mdl` files into isee-compatable XMILE files. \ No newline at end of file diff --git a/src/xmutil/build.rs b/src/xmutil/build.rs deleted file mode 100644 index e7ca7a3fb..000000000 --- a/src/xmutil/build.rs +++ /dev/null @@ -1,218 +0,0 @@ -// Copyright 2026 The Simlin Authors. All rights reserved. -// Use of this source code is governed by the Apache License, -// Version 2.0, that can be found in the LICENSE file. - -fn main() { - // Check if ASAN is enabled via environment variable - let asan_enabled = std::env::var("ASAN") - .map(|v| v == "1" || v.to_lowercase() == "true") - .unwrap_or(false); - - if asan_enabled { - println!("cargo:warning=ASAN enabled for xmutil C/C++ code"); - } - - let mut xmutil_build = cc::Build::new(); - xmutil_build - .cpp(true) - .include("./third_party/tinyxml2") - .include("./third_party") - .flag_if_supported("-std=c++17") - .flag_if_supported("-fno-omit-frame-pointer") - .flag_if_supported("-Wunused-private-field") - .flag_if_supported("-Wno-attributes") - .flag_if_supported("-Wno-unused-variable") - .flag_if_supported("-Wno-unused-but-set-variable") - .flag_if_supported("-Wno-sign-compare") - .flag_if_supported("-Wno-unused-local-typedefs") - .flag_if_supported("-Wno-unused-parameter") - .flag_if_supported("-Wno-unused-function") - .flag_if_supported("-Wno-unknown-pragmas") - .flag_if_supported("-Wno-parentheses") - .define("NDEBUG", None) - .file("./third_party/xmutil/Function/TableFunction.cpp") - .file("./third_party/xmutil/Function/Level.cpp") - .file("./third_party/xmutil/Function/State.cpp") - .file("./third_party/xmutil/Function/Function.cpp") - .file("./third_party/xmutil/Xmile/XMILEGenerator.cpp") - .file("./third_party/xmutil/Vensim/VensimView.cpp") - .file("./third_party/xmutil/Vensim/VensimParseFunctions.cpp") - .file("./third_party/xmutil/Vensim/VYacc.tab.cpp") - .file("./third_party/xmutil/Vensim/VensimLex.cpp") - .file("./third_party/xmutil/Vensim/VensimParse.cpp") - .file("./third_party/xmutil/Dynamo/DYacc.tab.cpp") - .file("./third_party/xmutil/Dynamo/DynamoFunction.cpp") - .file("./third_party/xmutil/Dynamo/DynamoLex.cpp") - .file("./third_party/xmutil/Dynamo/DynamoParse.cpp") - .file("./third_party/xmutil/Dynamo/DynamoParseFunctions.cpp") - .file("./third_party/xmutil/Model.cpp") - .file("./third_party/xmutil/Log.cpp") - .file("./third_party/xmutil/Unicode.cpp") - .file("./third_party/xmutil/ContextInfo.cpp") - .file("./third_party/xmutil/XMUtil.cpp") - .file("./third_party/xmutil/Symbol/Symbol.cpp") - .file("./third_party/xmutil/Symbol/SymbolTableBase.cpp") - .file("./third_party/xmutil/Symbol/SymbolList.cpp") - .file("./third_party/xmutil/Symbol/Units.cpp") - .file("./third_party/xmutil/Symbol/NotUsed_SymAllocList.cpp") - .file("./third_party/xmutil/Symbol/SymbolNameSpace.cpp") - .file("./third_party/xmutil/Symbol/SymbolListList.cpp") - .file("./third_party/xmutil/Symbol/LeftHandSide.cpp") - .file("./third_party/xmutil/Symbol/ExpressionList.cpp") - .file("./third_party/xmutil/Symbol/Expression.cpp") - .file("./third_party/xmutil/Symbol/Equation.cpp") - .file("./third_party/xmutil/Symbol/Variable.cpp") - .file("./third_party/xmutil/Symbol/UnitExpression.cpp"); - - // Add ASAN flags if enabled - if asan_enabled { - xmutil_build.flag_if_supported("-fsanitize=address"); - } - - let mut tinyxml_build = cc::Build::new(); - tinyxml_build - .cpp(true) - .flag_if_supported("-fno-omit-frame-pointer") - .file("./third_party/tinyxml2/tinyxml2.cpp"); - - // Add ASAN flags if enabled - if asan_enabled { - tinyxml_build.flag_if_supported("-fsanitize=address"); - } - - let target = std::env::var("TARGET").unwrap(); - if target.starts_with("wasm") { - // xmutil_build.cpp_set_stdlib("c++"); - xmutil_build.cpp_link_stdlib("c++-noexcept"); - // tinyxml_build.cpp_set_stdlib("c++"); - tinyxml_build.cpp_link_stdlib("c++-noexcept"); - println!( - "cargo:rustc-link-search={}/src/emscripten/cache/wasm/", - std::env::var("HOME").unwrap() - ); - println!("cargo:rustc-link-lib=c"); - } - - xmutil_build.compile("xmutil-native"); - tinyxml_build.compile("tinyxml-native"); - - let mut libutf_build = cc::Build::new(); - libutf_build - .flag_if_supported("-fno-omit-frame-pointer") - .flag_if_supported("-Wno-parentheses") - .flag_if_supported("-Wno-sign-compare") - .file("third_party/libutf/rune.c") - .file("third_party/libutf/runestrcat.c") - .file("third_party/libutf/runestrchr.c") - .file("third_party/libutf/runestrcmp.c") - .file("third_party/libutf/runestrcpy.c") - .file("third_party/libutf/runestrdup.c") - .file("third_party/libutf/runestrecpy.c") - .file("third_party/libutf/runestrlen.c") - .file("third_party/libutf/runestrncat.c") - .file("third_party/libutf/runestrncmp.c") - .file("third_party/libutf/runestrncpy.c") - .file("third_party/libutf/runestrrchr.c") - .file("third_party/libutf/runestrstr.c") - .file("third_party/libutf/runetype.c") - .file("third_party/libutf/utfecpy.c") - .file("third_party/libutf/utflen.c") - .file("third_party/libutf/utfnlen.c") - .file("third_party/libutf/utfrrune.c") - .file("third_party/libutf/utfrune.c") - .file("third_party/libutf/utfutf.c"); - - // Add ASAN flags if enabled - if asan_enabled { - libutf_build.flag_if_supported("-fsanitize=address"); - } - - libutf_build.compile("libutf-native"); - - println!("cargo:rerun-if-changed=build.rs"); - - println!("cargo:rerun-if-changed=third_party/libutf/rune.c"); - println!("cargo:rerun-if-changed=third_party/libutf/runestrcat.c"); - println!("cargo:rerun-if-changed=third_party/libutf/runestrchr.c"); - println!("cargo:rerun-if-changed=third_party/libutf/runestrcmp.c"); - println!("cargo:rerun-if-changed=third_party/libutf/runestrcpy.c"); - println!("cargo:rerun-if-changed=third_party/libutf/runestrdup.c"); - println!("cargo:rerun-if-changed=third_party/libutf/runestrecpy.c"); - println!("cargo:rerun-if-changed=third_party/libutf/runestrlen.c"); - println!("cargo:rerun-if-changed=third_party/libutf/runestrncat.c"); - println!("cargo:rerun-if-changed=third_party/libutf/runestrncmp.c"); - println!("cargo:rerun-if-changed=third_party/libutf/runestrncpy.c"); - println!("cargo:rerun-if-changed=third_party/libutf/runestrrchr.c"); - println!("cargo:rerun-if-changed=third_party/libutf/runestrstr.c"); - println!("cargo:rerun-if-changed=third_party/libutf/runetype.c"); - println!("cargo:rerun-if-changed=third_party/libutf/utfecpy.c"); - println!("cargo:rerun-if-changed=third_party/libutf/utflen.c"); - println!("cargo:rerun-if-changed=third_party/libutf/utfnlen.c"); - println!("cargo:rerun-if-changed=third_party/libutf/utfrrune.c"); - println!("cargo:rerun-if-changed=third_party/libutf/utfrune.c"); - println!("cargo:rerun-if-changed=third_party/libutf/utfutf.c"); - println!("cargo:rerun-if-changed=third_party/tinyxml2/tinyxml2.cpp"); - println!("cargo:rerun-if-changed=third_party/xmutil/ContextInfo.cpp"); - println!("cargo:rerun-if-changed=third_party/xmutil/Function/Function.cpp"); - println!("cargo:rerun-if-changed=third_party/xmutil/Function/Level.cpp"); - println!("cargo:rerun-if-changed=third_party/xmutil/Function/State.cpp"); - println!("cargo:rerun-if-changed=third_party/xmutil/Function/TableFunction.cpp"); - println!("cargo:rerun-if-changed=third_party/xmutil/Log.cpp"); - println!("cargo:rerun-if-changed=third_party/xmutil/Model.cpp"); - println!("cargo:rerun-if-changed=third_party/xmutil/Symbol/Equation.cpp"); - println!("cargo:rerun-if-changed=third_party/xmutil/Symbol/Expression.cpp"); - println!("cargo:rerun-if-changed=third_party/xmutil/Symbol/ExpressionList.cpp"); - println!("cargo:rerun-if-changed=third_party/xmutil/Symbol/LeftHandSide.cpp"); - println!("cargo:rerun-if-changed=third_party/xmutil/Symbol/NotUsed_SymAllocList.cpp"); - println!("cargo:rerun-if-changed=third_party/xmutil/Symbol/Symbol.cpp"); - println!("cargo:rerun-if-changed=third_party/xmutil/Symbol/SymbolList.cpp"); - println!("cargo:rerun-if-changed=third_party/xmutil/Symbol/SymbolListList.cpp"); - println!("cargo:rerun-if-changed=third_party/xmutil/Symbol/SymbolNameSpace.cpp"); - println!("cargo:rerun-if-changed=third_party/xmutil/Symbol/SymbolTableBase.cpp"); - println!("cargo:rerun-if-changed=third_party/xmutil/Symbol/UnitExpression.cpp"); - println!("cargo:rerun-if-changed=third_party/xmutil/Symbol/Units.cpp"); - println!("cargo:rerun-if-changed=third_party/xmutil/Symbol/Variable.cpp"); - println!("cargo:rerun-if-changed=third_party/xmutil/Unicode.cpp"); - println!("cargo:rerun-if-changed=third_party/xmutil/Vensim/VensimLex.cpp"); - println!("cargo:rerun-if-changed=third_party/xmutil/Vensim/VensimParse.cpp"); - println!("cargo:rerun-if-changed=third_party/xmutil/Vensim/VensimParseFunctions.cpp"); - println!("cargo:rerun-if-changed=third_party/xmutil/Vensim/VensimView.cpp"); - println!("cargo:rerun-if-changed=third_party/xmutil/Vensim/VYacc.tab.cpp"); - println!("cargo:rerun-if-changed=third_party/xmutil/Xmile/XMILEGenerator.cpp"); - println!("cargo:rerun-if-changed=third_party/xmutil/XMUtil.cpp"); - - println!("cargo:rerun-if-changed=third_party/libutf/lib9.h"); - println!("cargo:rerun-if-changed=third_party/libutf/plan9.h"); - println!("cargo:rerun-if-changed=third_party/libutf/utfdef.h"); - println!("cargo:rerun-if-changed=third_party/libutf/utf.h"); - println!("cargo:rerun-if-changed=third_party/tinyxml2/tinyxml2.h"); - println!("cargo:rerun-if-changed=third_party/xmutil/ContextInfo.h"); - println!("cargo:rerun-if-changed=third_party/xmutil/Function/Function.h"); - println!("cargo:rerun-if-changed=third_party/xmutil/Function/Level.h"); - println!("cargo:rerun-if-changed=third_party/xmutil/Function/State.h"); - println!("cargo:rerun-if-changed=third_party/xmutil/Function/TableFunction.h"); - println!("cargo:rerun-if-changed=third_party/xmutil/Log.h"); - println!("cargo:rerun-if-changed=third_party/xmutil/Model.h"); - println!("cargo:rerun-if-changed=third_party/xmutil/Symbol/Equation.h"); - println!("cargo:rerun-if-changed=third_party/xmutil/Symbol/Expression.h"); - println!("cargo:rerun-if-changed=third_party/xmutil/Symbol/ExpressionList.h"); - println!("cargo:rerun-if-changed=third_party/xmutil/Symbol/LeftHandSide.h"); - println!("cargo:rerun-if-changed=third_party/xmutil/Symbol/NotUsed_SymAllocList.h"); - println!("cargo:rerun-if-changed=third_party/xmutil/Symbol/Parse.h"); - println!("cargo:rerun-if-changed=third_party/xmutil/Symbol/Symbol.h"); - println!("cargo:rerun-if-changed=third_party/xmutil/Symbol/SymbolList.h"); - println!("cargo:rerun-if-changed=third_party/xmutil/Symbol/SymbolListList.h"); - println!("cargo:rerun-if-changed=third_party/xmutil/Symbol/SymbolNameSpace.h"); - println!("cargo:rerun-if-changed=third_party/xmutil/Symbol/SymbolTableBase.h"); - println!("cargo:rerun-if-changed=third_party/xmutil/Symbol/UnitExpression.h"); - println!("cargo:rerun-if-changed=third_party/xmutil/Symbol/Units.h"); - println!("cargo:rerun-if-changed=third_party/xmutil/Symbol/Variable.h"); - println!("cargo:rerun-if-changed=third_party/xmutil/Unicode.h"); - println!("cargo:rerun-if-changed=third_party/xmutil/Vensim/VensimLex.h"); - println!("cargo:rerun-if-changed=third_party/xmutil/Vensim/VensimParseFunctions.h"); - println!("cargo:rerun-if-changed=third_party/xmutil/Vensim/VensimParse.h"); - println!("cargo:rerun-if-changed=third_party/xmutil/Vensim/VensimView.h"); - println!("cargo:rerun-if-changed=third_party/xmutil/Vensim/VYacc.tab.hpp"); - println!("cargo:rerun-if-changed=third_party/xmutil/Xmile/XMILEGenerator.h"); - println!("cargo:rerun-if-changed=third_party/xmutil/XMUtil.h"); -} diff --git a/src/xmutil/src/lib.rs b/src/xmutil/src/lib.rs deleted file mode 100644 index 7d4ffa9fb..000000000 --- a/src/xmutil/src/lib.rs +++ /dev/null @@ -1,203 +0,0 @@ -// Copyright 2026 The Simlin Authors. All rights reserved. -// Use of this source code is governed by the Apache License, -// Version 2.0, that can be found in the LICENSE file. - -use std::ffi::{CStr, CString}; -use std::os::raw::c_char; -use std::str; -use std::sync::Mutex; - -unsafe extern "C" { - fn xmutil_convert_mdl_to_xmile( - mdl_source: *const u8, - mdl_source_len: u32, - file_name: *const u8, - is_compact: bool, - is_long_name: bool, - is_as_sectors: bool, - ) -> *mut c_char; - fn xmutil_get_log() -> *const c_char; - fn xmutil_clear_log(); -} - -// xmutil isn't thread-safe, so we need to synchronize calls into it. -static LOCK: Mutex<()> = Mutex::new(()); - -pub fn convert_vensim_mdl(mdl_source: &str, is_compact: bool) -> (Option, Option) { - // always grab the lock guard before calling in to _convert_mdl_to_xmile - let _guard = LOCK.lock().unwrap(); - - let str_ptr = mdl_source.as_ptr(); - let str_len = mdl_source.len() as u32; - - unsafe { - // ensure the log starts empty - xmutil_clear_log(); - - let result_buf = xmutil_convert_mdl_to_xmile( - str_ptr, - str_len, - std::ptr::null(), - is_compact, - false, - true, - ); - - let log_buf = xmutil_get_log(); - let log = if !log_buf.is_null() { - // a reference to non-owned data - let c_str: &CStr = CStr::from_ptr(log_buf); - let str_slice: &str = c_str.to_str().unwrap(); - if str_slice.is_empty() { - None - } else { - Some(str_slice.to_owned()) - } - } else { - None - }; - - let model = if !result_buf.is_null() { - // take ownership of the returned string - CString::from_raw(result_buf) - .into_string() - .ok() - .filter(|s| !s.is_empty()) - } else { - None - }; - - (model, log) - } -} - -#[cfg(test)] -mod tests { - #[test] - fn it_works() { - let mdl_source = "{UTF-8} -Inflow= - IF THEN ELSE(Time = INITIAL TIME , 10 , 3 ) - ~ - ~ | - -Outflow 1= - Stock/TIME STEP - ~ - ~ | - -Outflow 2= - IF THEN ELSE( Time = FINAL TIME , 2 , 0 ) - ~ - ~ | - -Stock= INTEG ( - Inflow-Outflow 1-Outflow 2, - 0) - ~ - ~ | - -******************************************************** - .Control -********************************************************~ - Simulation Control Parameters - | - -FINAL TIME = 10 - ~ Month - ~ The final time for the simulation. - | - -INITIAL TIME = 0 - ~ Month - ~ The initial time for the simulation. - | - -SAVEPER = - TIME STEP - ~ Month [0,?] - ~ The frequency with which output is stored. - | - -TIME STEP = 1 - ~ Month [0,?] - ~ The time step for the simulation. - | - -\\\\\\---/// Sketch information - do not modify anything except names -V300 Do not put anything below this section - it will be ignored -*View 1 -$192-192-192,0,Times New Roman|12||0-0-0|0-0-0|0-0-255|-1--1--1|-1--1--1|72,72,100,0 -10,1,Stock,351,249,40,20,3,3,0,0,0,0,0,0 -12,2,48,513,251,10,8,0,3,0,0,-1,0,0,0 -1,4,6,2,4,0,0,22,0,0,0,-1--1--1,,1|(478,251)| -1,5,6,1,100,0,0,22,0,0,0,-1--1--1,,1|(416,251)| -11,6,0,447,251,6,8,34,3,0,0,1,0,0,0 -10,7,Outflow 1,447,267,27,8,40,3,0,0,-1,0,0,0 -1,8,1,7,1,0,0,0,0,128,0,-1--1--1,,1|(394,294)| -10,9,TIME STEP,485,317,39,8,8,2,0,3,-1,0,0,0,128-128-128,0-0-0,|0||128-128-128 -1,10,9,7,0,0,0,0,0,128,0,-1--1--1,,1|(470,297)| -12,11,48,191,244,10,8,0,3,0,0,-1,0,0,0 -1,13,15,1,4,0,0,22,0,0,0,-1--1--1,,1|(286,244)| -1,14,15,11,100,0,0,22,0,0,0,-1--1--1,,1|(225,244)| -11,15,0,256,244,6,8,34,3,0,0,1,0,0,0 -10,16,Inflow,256,260,18,8,40,3,0,0,-1,0,0,0 -10,17,INITIAL TIME,190,309,47,8,8,2,0,3,-1,0,0,0,128-128-128,0-0-0,|0||128-128-128 -1,18,17,16,0,0,0,0,0,128,0,-1--1--1,,1|(216,288)| -10,19,Time,292,310,21,8,8,2,0,3,-1,0,0,0,128-128-128,0-0-0,|0||128-128-128 -1,20,19,16,0,0,0,0,0,128,0,-1--1--1,,1|(278,290)| -12,21,48,355,133,10,8,0,3,0,0,-1,0,0,0 -1,23,25,21,4,0,0,22,0,0,0,-1--1--1,,1|(356,159)| -1,24,25,1,100,0,0,22,0,0,0,-1--1--1,,1|(356,209)| -11,25,0,356,183,8,6,33,3,0,0,4,0,0,0 -10,26,Outflow 2,391,183,27,8,40,3,0,0,-1,0,0,0 -10,27,Time,428,131,21,8,8,2,0,3,-1,0,0,0,128-128-128,0-0-0,|0||128-128-128 -10,28,FINAL TIME,494,188,43,8,8,2,0,3,-1,0,0,0,128-128-128,0-0-0,|0||128-128-128 -1,29,28,26,0,0,0,0,0,128,0,-1--1--1,,1|(441,185)| -1,30,27,26,0,0,0,0,0,128,0,-1--1--1,,1|(413,151)| -///---\\\\\\ -:L<%^E!@ -1:Current.vdf -9:Current -22:$,Dollar,Dollars,$s -22:Day,Days -22:Hour,Hours -22:Month,Months -22:Person,People,Persons -22:Unit,Units -22:Week,Weeks -22:Year,Years -15:0,0,0,0,0,0 -19:100,0 -27:2, -34:0, -4:Time -5:Stock -35:Date -36:YYYY-MM-DD -37:2000 -38:1 -39:1 -40:2 -41:0 -42:1 -24:0 -25:10 -26:10 -"; - - let (actual, logs) = crate::convert_vensim_mdl(mdl_source, false); - let actual = actual.unwrap(); - assert!(actual.starts_with("\n")); - assert!(logs.is_none()); - } - - #[test] - fn failure_is_none() { - let (xmile, logs) = crate::convert_vensim_mdl(":ohno:", true); - - assert!(xmile.is_none()); - assert!(logs.is_some()); - } -} diff --git a/src/xmutil/third_party/libutf/LICENSE b/src/xmutil/third_party/libutf/LICENSE deleted file mode 100644 index ad76cd52b..000000000 --- a/src/xmutil/third_party/libutf/LICENSE +++ /dev/null @@ -1,13 +0,0 @@ -/* - * The authors of this software are Rob Pike and Ken Thompson. - * Copyright (c) 1998-2002 by Lucent Technologies. - * Permission to use, copy, modify, and distribute this software for any - * purpose without fee is hereby granted, provided that this entire notice - * is included in all copies of any software which is or includes a copy - * or modification of this software and in all copies of the supporting - * documentation for such software. - * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED - * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE ANY - * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY - * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. - */ diff --git a/src/xmutil/third_party/libutf/NOTICE b/src/xmutil/third_party/libutf/NOTICE deleted file mode 100644 index ad76cd52b..000000000 --- a/src/xmutil/third_party/libutf/NOTICE +++ /dev/null @@ -1,13 +0,0 @@ -/* - * The authors of this software are Rob Pike and Ken Thompson. - * Copyright (c) 1998-2002 by Lucent Technologies. - * Permission to use, copy, modify, and distribute this software for any - * purpose without fee is hereby granted, provided that this entire notice - * is included in all copies of any software which is or includes a copy - * or modification of this software and in all copies of the supporting - * documentation for such software. - * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED - * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE ANY - * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY - * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. - */ diff --git a/src/xmutil/third_party/libutf/README b/src/xmutil/third_party/libutf/README deleted file mode 100644 index ad76cd52b..000000000 --- a/src/xmutil/third_party/libutf/README +++ /dev/null @@ -1,13 +0,0 @@ -/* - * The authors of this software are Rob Pike and Ken Thompson. - * Copyright (c) 1998-2002 by Lucent Technologies. - * Permission to use, copy, modify, and distribute this software for any - * purpose without fee is hereby granted, provided that this entire notice - * is included in all copies of any software which is or includes a copy - * or modification of this software and in all copies of the supporting - * documentation for such software. - * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED - * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE ANY - * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY - * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. - */ diff --git a/src/xmutil/third_party/libutf/lib9.h b/src/xmutil/third_party/libutf/lib9.h deleted file mode 100644 index a4a18d26e..000000000 --- a/src/xmutil/third_party/libutf/lib9.h +++ /dev/null @@ -1,17 +0,0 @@ -#include - -#include "utf.h" - -#define nil ((void *)0) - -#define uchar _fmtuchar -#define ushort _fmtushort -#define uint _fmtuint -#define ulong _fmtulong -#define vlong _fmtvlong -#define uvlong _fmtuvlong - -typedef unsigned char uchar; -typedef unsigned short ushort; -typedef unsigned int uint; -typedef unsigned long ulong; diff --git a/src/xmutil/third_party/libutf/plan9.h b/src/xmutil/third_party/libutf/plan9.h deleted file mode 100644 index 9e714d728..000000000 --- a/src/xmutil/third_party/libutf/plan9.h +++ /dev/null @@ -1,31 +0,0 @@ -/* - * compiler directive on Plan 9 - */ -#ifndef USED -#define USED(x) \ - if (x) \ - ; \ - else -#endif - -/* - * easiest way to make sure these are defined - */ -#define uchar _utfuchar -#define ushort _utfushort -#define uint _utfuint -#define ulong _utfulong -typedef unsigned char uchar; -typedef unsigned short ushort; -typedef unsigned int uint; -typedef unsigned long ulong; - -/* - * nil cannot be ((void*)0) on ANSI C, - * because it is used for function pointers - */ -#undef nil -#define nil 0 - -#undef nelem -#define nelem(x) (sizeof(x) / sizeof(x)[0]) diff --git a/src/xmutil/third_party/libutf/portdate b/src/xmutil/third_party/libutf/portdate deleted file mode 100644 index 1385cf06c..000000000 --- a/src/xmutil/third_party/libutf/portdate +++ /dev/null @@ -1,20 +0,0 @@ -rune.c 2004/1225 -runestrcat.c 2004/1225 -runestrchr.c 2004/1225 -runestrcmp.c 2004/1225 -runestrcpy.c 2004/1225 -runestrdup.c 2004/1225 -runestrecpy.c 2004/1225 -runestrlen.c 2004/1225 -runestrncat.c 2004/1225 -runestrncmp.c 2004/1225 -runestrncpy.c 2004/1225 -runestrrchr.c 2004/1225 -runestrstr.c 2004/1225 -runetype.c 2004/1225 -utfecpy.c 2004/1225 -utflen.c 2004/1225 -utfnlen.c 2004/1225 -utfrrune.c 2004/1225 -utfrune.c 2004/1225 -utfutf.c 2004/1225 diff --git a/src/xmutil/third_party/libutf/rune.c b/src/xmutil/third_party/libutf/rune.c deleted file mode 100644 index 89fdb8a3f..000000000 --- a/src/xmutil/third_party/libutf/rune.c +++ /dev/null @@ -1,217 +0,0 @@ -/* - * The authors of this software are Rob Pike and Ken Thompson. - * Copyright (c) 2002 by Lucent Technologies. - * Permission to use, copy, modify, and distribute this software for any - * purpose without fee is hereby granted, provided that this entire notice - * is included in all copies of any software which is or includes a copy - * or modification of this software and in all copies of the supporting - * documentation for such software. - * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED - * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE - * ANY REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY - * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. - */ -#include -#include -#include "plan9.h" -#include "utf.h" - -enum -{ - Bit1 = 7, - Bitx = 6, - Bit2 = 5, - Bit3 = 4, - Bit4 = 3, - Bit5 = 2, - - T1 = ((1<<(Bit1+1))-1) ^ 0xFF, /* 0000 0000 */ - Tx = ((1<<(Bitx+1))-1) ^ 0xFF, /* 1000 0000 */ - T2 = ((1<<(Bit2+1))-1) ^ 0xFF, /* 1100 0000 */ - T3 = ((1<<(Bit3+1))-1) ^ 0xFF, /* 1110 0000 */ - T4 = ((1<<(Bit4+1))-1) ^ 0xFF, /* 1111 0000 */ - T5 = ((1<<(Bit5+1))-1) ^ 0xFF, /* 1111 1000 */ - - Rune1 = (1<<(Bit1+0*Bitx))-1, /* 0000 0000 0000 0000 0111 1111 */ - Rune2 = (1<<(Bit2+1*Bitx))-1, /* 0000 0000 0000 0111 1111 1111 */ - Rune3 = (1<<(Bit3+2*Bitx))-1, /* 0000 0000 1111 1111 1111 1111 */ - Rune4 = (1<<(Bit4+3*Bitx))-1, /* 0011 1111 1111 1111 1111 1111 */ - - Maskx = (1< T1 - */ - c = *(uchar*)str; - if(c < Tx) { - *rune = c; - return 1; - } - - /* - * two character sequence - * 0080-07FF => T2 Tx - */ - c1 = *(uchar*)(str+1) ^ Tx; - if(c1 & Testx) - goto bad; - if(c < T3) { - if(c < T2) - goto bad; - l = ((c << Bitx) | c1) & Rune2; - if(l <= Rune1) - goto bad; - *rune = l; - return 2; - } - - /* - * three character sequence - * 0800-FFFF => T3 Tx Tx - */ - c2 = *(uchar*)(str+2) ^ Tx; - if(c2 & Testx) - goto bad; - if(c < T4) { - l = ((((c << Bitx) | c1) << Bitx) | c2) & Rune3; - if(l <= Rune2) - goto bad; - *rune = l; - return 3; - } - - /* - * four character sequence - * 10000-10FFFF => T4 Tx Tx Tx - */ - if(UTFmax >= 4) { - c3 = *(uchar*)(str+3) ^ Tx; - if(c3 & Testx) - goto bad; - if(c < T5) { - l = ((((((c << Bitx) | c1) << Bitx) | c2) << Bitx) | c3) & Rune4; - if(l <= Rune3) - goto bad; - if(l > Runemax) - goto bad; - *rune = l; - return 4; - } - } - - /* - * bad decoding - */ -bad: - *rune = Bad; - return 1; -} - -int -runetochar(char *str, Rune *rune) -{ - long c; - - /* - * one character sequence - * 00000-0007F => 00-7F - */ - c = *rune; - if(c <= Rune1) { - str[0] = c; - return 1; - } - - /* - * two character sequence - * 00080-007FF => T2 Tx - */ - if(c <= Rune2) { - str[0] = T2 | (c >> 1*Bitx); - str[1] = Tx | (c & Maskx); - return 2; - } - - /* - * three character sequence - * 00800-0FFFF => T3 Tx Tx - */ - if(c > Runemax) - c = Runeerror; - if(c <= Rune3) { - str[0] = T3 | (c >> 2*Bitx); - str[1] = Tx | ((c >> 1*Bitx) & Maskx); - str[2] = Tx | (c & Maskx); - return 3; - } - - /* - * four character sequence - * 010000-1FFFFF => T4 Tx Tx Tx - */ - str[0] = T4 | (c >> 3*Bitx); - str[1] = Tx | ((c >> 2*Bitx) & Maskx); - str[2] = Tx | ((c >> 1*Bitx) & Maskx); - str[3] = Tx | (c & Maskx); - return 4; -} - -int -runelen(long c) -{ - Rune rune; - char str[10]; - - rune = c; - return runetochar(str, &rune); -} - -int -runenlen(Rune *r, int nrune) -{ - int nb, c; - - nb = 0; - while(nrune--) { - c = *r++; - if(c <= Rune1) - nb++; - else - if(c <= Rune2) - nb += 2; - else - if(c <= Rune3 || c > Runemax) - nb += 3; - else - nb += 4; - } - return nb; -} - -int -fullrune(char *str, int n) -{ - int c; - - if(n <= 0) - return 0; - c = *(uchar*)str; - if(c < Tx) - return 1; - if(c < T3) - return n >= 2; - if(UTFmax == 3 || c < T4) - return n >= 3; - return n >= 4; -} diff --git a/src/xmutil/third_party/libutf/runestrcat.c b/src/xmutil/third_party/libutf/runestrcat.c deleted file mode 100644 index 65d4c0fb7..000000000 --- a/src/xmutil/third_party/libutf/runestrcat.c +++ /dev/null @@ -1,25 +0,0 @@ -/* - * The authors of this software are Rob Pike and Ken Thompson. - * Copyright (c) 2002 by Lucent Technologies. - * Permission to use, copy, modify, and distribute this software for any - * purpose without fee is hereby granted, provided that this entire notice - * is included in all copies of any software which is or includes a copy - * or modification of this software and in all copies of the supporting - * documentation for such software. - * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED - * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE - * ANY REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY - * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. - */ -#include -#include -#include "plan9.h" -#include "utf.h" - -Rune* -runestrcat(Rune *s1, Rune *s2) -{ - - runestrcpy(runestrchr(s1, 0), s2); - return s1; -} diff --git a/src/xmutil/third_party/libutf/runestrchr.c b/src/xmutil/third_party/libutf/runestrchr.c deleted file mode 100644 index 9d4d2b851..000000000 --- a/src/xmutil/third_party/libutf/runestrchr.c +++ /dev/null @@ -1,35 +0,0 @@ -/* - * The authors of this software are Rob Pike and Ken Thompson. - * Copyright (c) 2002 by Lucent Technologies. - * Permission to use, copy, modify, and distribute this software for any - * purpose without fee is hereby granted, provided that this entire notice - * is included in all copies of any software which is or includes a copy - * or modification of this software and in all copies of the supporting - * documentation for such software. - * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED - * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE - * ANY REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY - * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. - */ -#include -#include -#include "plan9.h" -#include "utf.h" - -Rune* -runestrchr(Rune *s, Rune c) -{ - Rune c0 = c; - Rune c1; - - if(c == 0) { - while(*s++) - ; - return s-1; - } - - while ((c1 = *s++)) - if(c1 == c0) - return s-1; - return 0; -} diff --git a/src/xmutil/third_party/libutf/runestrcmp.c b/src/xmutil/third_party/libutf/runestrcmp.c deleted file mode 100644 index a36861315..000000000 --- a/src/xmutil/third_party/libutf/runestrcmp.c +++ /dev/null @@ -1,35 +0,0 @@ -/* - * The authors of this software are Rob Pike and Ken Thompson. - * Copyright (c) 2002 by Lucent Technologies. - * Permission to use, copy, modify, and distribute this software for any - * purpose without fee is hereby granted, provided that this entire notice - * is included in all copies of any software which is or includes a copy - * or modification of this software and in all copies of the supporting - * documentation for such software. - * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED - * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE - * ANY REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY - * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. - */ -#include -#include -#include "plan9.h" -#include "utf.h" - -int -runestrcmp(Rune *s1, Rune *s2) -{ - Rune c1, c2; - - for(;;) { - c1 = *s1++; - c2 = *s2++; - if(c1 != c2) { - if(c1 > c2) - return 1; - return -1; - } - if(c1 == 0) - return 0; - } -} diff --git a/src/xmutil/third_party/libutf/runestrcpy.c b/src/xmutil/third_party/libutf/runestrcpy.c deleted file mode 100644 index b7388d1b7..000000000 --- a/src/xmutil/third_party/libutf/runestrcpy.c +++ /dev/null @@ -1,28 +0,0 @@ -/* - * The authors of this software are Rob Pike and Ken Thompson. - * Copyright (c) 2002 by Lucent Technologies. - * Permission to use, copy, modify, and distribute this software for any - * purpose without fee is hereby granted, provided that this entire notice - * is included in all copies of any software which is or includes a copy - * or modification of this software and in all copies of the supporting - * documentation for such software. - * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED - * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE - * ANY REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY - * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. - */ -#include -#include -#include "plan9.h" -#include "utf.h" - -Rune* -runestrcpy(Rune *s1, Rune *s2) -{ - Rune *os1; - - os1 = s1; - while((*s1++ = *s2++)) - ; - return os1; -} diff --git a/src/xmutil/third_party/libutf/runestrdup.c b/src/xmutil/third_party/libutf/runestrdup.c deleted file mode 100644 index 4f9d6f424..000000000 --- a/src/xmutil/third_party/libutf/runestrdup.c +++ /dev/null @@ -1,30 +0,0 @@ -/* - * The authors of this software are Rob Pike and Ken Thompson. - * Copyright (c) 2002 by Lucent Technologies. - * Permission to use, copy, modify, and distribute this software for any - * purpose without fee is hereby granted, provided that this entire notice - * is included in all copies of any software which is or includes a copy - * or modification of this software and in all copies of the supporting - * documentation for such software. - * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED - * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE - * ANY REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY - * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. - */ -#include -#include -#include -#include "plan9.h" -#include "utf.h" - -Rune* -runestrdup(Rune *s) -{ - Rune *ns; - - ns = malloc(sizeof(Rune)*(runestrlen(s) + 1)); - if(ns == 0) - return 0; - - return runestrcpy(ns, s); -} diff --git a/src/xmutil/third_party/libutf/runestrecpy.c b/src/xmutil/third_party/libutf/runestrecpy.c deleted file mode 100644 index ce0b64527..000000000 --- a/src/xmutil/third_party/libutf/runestrecpy.c +++ /dev/null @@ -1,32 +0,0 @@ -/* - * The authors of this software are Rob Pike and Ken Thompson. - * Copyright (c) 2002 by Lucent Technologies. - * Permission to use, copy, modify, and distribute this software for any - * purpose without fee is hereby granted, provided that this entire notice - * is included in all copies of any software which is or includes a copy - * or modification of this software and in all copies of the supporting - * documentation for such software. - * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED - * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE - * ANY REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY - * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. - */ -#include -#include -#include "plan9.h" -#include "utf.h" - -Rune* -runestrecpy(Rune *s1, Rune *es1, Rune *s2) -{ - if(s1 >= es1) - return s1; - - while((*s1++ = *s2++)){ - if(s1 == es1){ - *--s1 = '\0'; - break; - } - } - return s1; -} diff --git a/src/xmutil/third_party/libutf/runestrlen.c b/src/xmutil/third_party/libutf/runestrlen.c deleted file mode 100644 index 0a13ecd80..000000000 --- a/src/xmutil/third_party/libutf/runestrlen.c +++ /dev/null @@ -1,24 +0,0 @@ -/* - * The authors of this software are Rob Pike and Ken Thompson. - * Copyright (c) 2002 by Lucent Technologies. - * Permission to use, copy, modify, and distribute this software for any - * purpose without fee is hereby granted, provided that this entire notice - * is included in all copies of any software which is or includes a copy - * or modification of this software and in all copies of the supporting - * documentation for such software. - * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED - * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE - * ANY REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY - * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. - */ -#include -#include -#include "plan9.h" -#include "utf.h" - -long -runestrlen(Rune *s) -{ - - return runestrchr(s, 0) - s; -} diff --git a/src/xmutil/third_party/libutf/runestrncat.c b/src/xmutil/third_party/libutf/runestrncat.c deleted file mode 100644 index 264a0f0b1..000000000 --- a/src/xmutil/third_party/libutf/runestrncat.c +++ /dev/null @@ -1,32 +0,0 @@ -/* - * The authors of this software are Rob Pike and Ken Thompson. - * Copyright (c) 2002 by Lucent Technologies. - * Permission to use, copy, modify, and distribute this software for any - * purpose without fee is hereby granted, provided that this entire notice - * is included in all copies of any software which is or includes a copy - * or modification of this software and in all copies of the supporting - * documentation for such software. - * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED - * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE - * ANY REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY - * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. - */ -#include -#include -#include "plan9.h" -#include "utf.h" - -Rune* -runestrncat(Rune *s1, Rune *s2, long n) -{ - Rune *os1; - - os1 = s1; - s1 = runestrchr(s1, 0); - while((*s1++ = *s2++)) - if(--n < 0) { - s1[-1] = 0; - break; - } - return os1; -} diff --git a/src/xmutil/third_party/libutf/runestrncmp.c b/src/xmutil/third_party/libutf/runestrncmp.c deleted file mode 100644 index 5e9a3b603..000000000 --- a/src/xmutil/third_party/libutf/runestrncmp.c +++ /dev/null @@ -1,37 +0,0 @@ -/* - * The authors of this software are Rob Pike and Ken Thompson. - * Copyright (c) 2002 by Lucent Technologies. - * Permission to use, copy, modify, and distribute this software for any - * purpose without fee is hereby granted, provided that this entire notice - * is included in all copies of any software which is or includes a copy - * or modification of this software and in all copies of the supporting - * documentation for such software. - * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED - * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE - * ANY REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY - * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. - */ -#include -#include -#include "plan9.h" -#include "utf.h" - -int -runestrncmp(Rune *s1, Rune *s2, long n) -{ - Rune c1, c2; - - while(n > 0) { - c1 = *s1++; - c2 = *s2++; - n--; - if(c1 != c2) { - if(c1 > c2) - return 1; - return -1; - } - if(c1 == 0) - break; - } - return 0; -} diff --git a/src/xmutil/third_party/libutf/runestrncpy.c b/src/xmutil/third_party/libutf/runestrncpy.c deleted file mode 100644 index ffcb3e146..000000000 --- a/src/xmutil/third_party/libutf/runestrncpy.c +++ /dev/null @@ -1,33 +0,0 @@ -/* - * The authors of this software are Rob Pike and Ken Thompson. - * Copyright (c) 2002 by Lucent Technologies. - * Permission to use, copy, modify, and distribute this software for any - * purpose without fee is hereby granted, provided that this entire notice - * is included in all copies of any software which is or includes a copy - * or modification of this software and in all copies of the supporting - * documentation for such software. - * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED - * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE - * ANY REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY - * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. - */ -#include -#include -#include "plan9.h" -#include "utf.h" - -Rune* -runestrncpy(Rune *s1, Rune *s2, long n) -{ - int i; - Rune *os1; - - os1 = s1; - for(i = 0; i < n; i++) - if((*s1++ = *s2++) == 0) { - while(++i < n) - *s1++ = 0; - return os1; - } - return os1; -} diff --git a/src/xmutil/third_party/libutf/runestrrchr.c b/src/xmutil/third_party/libutf/runestrrchr.c deleted file mode 100644 index 18ca3d765..000000000 --- a/src/xmutil/third_party/libutf/runestrrchr.c +++ /dev/null @@ -1,30 +0,0 @@ -/* - * The authors of this software are Rob Pike and Ken Thompson. - * Copyright (c) 2002 by Lucent Technologies. - * Permission to use, copy, modify, and distribute this software for any - * purpose without fee is hereby granted, provided that this entire notice - * is included in all copies of any software which is or includes a copy - * or modification of this software and in all copies of the supporting - * documentation for such software. - * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED - * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE - * ANY REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY - * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. - */ -#include -#include -#include "plan9.h" -#include "utf.h" - -Rune* -runestrrchr(Rune *s, Rune c) -{ - Rune *r; - - if(c == 0) - return runestrchr(s, 0); - r = 0; - while((s = runestrchr(s, c))) - r = s++; - return r; -} diff --git a/src/xmutil/third_party/libutf/runestrstr.c b/src/xmutil/third_party/libutf/runestrstr.c deleted file mode 100644 index bff923979..000000000 --- a/src/xmutil/third_party/libutf/runestrstr.c +++ /dev/null @@ -1,44 +0,0 @@ -/* - * The authors of this software are Rob Pike and Ken Thompson. - * Copyright (c) 2002 by Lucent Technologies. - * Permission to use, copy, modify, and distribute this software for any - * purpose without fee is hereby granted, provided that this entire notice - * is included in all copies of any software which is or includes a copy - * or modification of this software and in all copies of the supporting - * documentation for such software. - * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED - * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE - * ANY REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY - * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. - */ -#include -#include -#include "plan9.h" -#include "utf.h" - -/* - * Return pointer to first occurrence of s2 in s1, - * 0 if none - */ -Rune* -runestrstr(Rune *s1, Rune *s2) -{ - Rune *p, *pa, *pb; - int c0, c; - - c0 = *s2; - if(c0 == 0) - return s1; - s2++; - for(p=runestrchr(s1, c0); p; p=runestrchr(p+1, c0)) { - pa = p; - for(pb=s2;; pb++) { - c = *pb; - if(c == 0) - return p; - if((unsigned int)c != *++pa) - break; - } - } - return 0; -} diff --git a/src/xmutil/third_party/libutf/runetype.c b/src/xmutil/third_party/libutf/runetype.c deleted file mode 100644 index ac6d7b576..000000000 --- a/src/xmutil/third_party/libutf/runetype.c +++ /dev/null @@ -1,1151 +0,0 @@ -/* - * The authors of this software are Rob Pike and Ken Thompson. - * Copyright (c) 2002 by Lucent Technologies. - * Permission to use, copy, modify, and distribute this software for any - * purpose without fee is hereby granted, provided that this entire notice - * is included in all copies of any software which is or includes a copy - * or modification of this software and in all copies of the supporting - * documentation for such software. - * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED - * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE - * ANY REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY - * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. - */ -#include -#include -#include "plan9.h" -#include "utf.h" - -/* - * alpha ranges - - * only covers ranges not in lower||upper - */ -static -Rune __alpha2[] = -{ - 0x00d8, 0x00f6, /* Ø - ö */ - 0x00f8, 0x01f5, /* ø - ǵ */ - 0x0250, 0x02a8, /* ɐ - ʨ */ - 0x038e, 0x03a1, /* Ύ - Ρ */ - 0x03a3, 0x03ce, /* Σ - ώ */ - 0x03d0, 0x03d6, /* ϐ - ϖ */ - 0x03e2, 0x03f3, /* Ϣ - ϳ */ - 0x0490, 0x04c4, /* Ґ - ӄ */ - 0x0561, 0x0587, /* ա - և */ - 0x05d0, 0x05ea, /* א - ת */ - 0x05f0, 0x05f2, /* װ - ײ */ - 0x0621, 0x063a, /* ء - غ */ - 0x0640, 0x064a, /* ـ - ي */ - 0x0671, 0x06b7, /* ٱ - ڷ */ - 0x06ba, 0x06be, /* ں - ھ */ - 0x06c0, 0x06ce, /* ۀ - ێ */ - 0x06d0, 0x06d3, /* ې - ۓ */ - 0x0905, 0x0939, /* अ - ह */ - 0x0958, 0x0961, /* क़ - ॡ */ - 0x0985, 0x098c, /* অ - ঌ */ - 0x098f, 0x0990, /* এ - ঐ */ - 0x0993, 0x09a8, /* ও - ন */ - 0x09aa, 0x09b0, /* প - র */ - 0x09b6, 0x09b9, /* শ - হ */ - 0x09dc, 0x09dd, /* ড় - ঢ় */ - 0x09df, 0x09e1, /* য় - ৡ */ - 0x09f0, 0x09f1, /* ৰ - ৱ */ - 0x0a05, 0x0a0a, /* ਅ - ਊ */ - 0x0a0f, 0x0a10, /* ਏ - ਐ */ - 0x0a13, 0x0a28, /* ਓ - ਨ */ - 0x0a2a, 0x0a30, /* ਪ - ਰ */ - 0x0a32, 0x0a33, /* ਲ - ਲ਼ */ - 0x0a35, 0x0a36, /* ਵ - ਸ਼ */ - 0x0a38, 0x0a39, /* ਸ - ਹ */ - 0x0a59, 0x0a5c, /* ਖ਼ - ੜ */ - 0x0a85, 0x0a8b, /* અ - ઋ */ - 0x0a8f, 0x0a91, /* એ - ઑ */ - 0x0a93, 0x0aa8, /* ઓ - ન */ - 0x0aaa, 0x0ab0, /* પ - ર */ - 0x0ab2, 0x0ab3, /* લ - ળ */ - 0x0ab5, 0x0ab9, /* વ - હ */ - 0x0b05, 0x0b0c, /* ଅ - ଌ */ - 0x0b0f, 0x0b10, /* ଏ - ଐ */ - 0x0b13, 0x0b28, /* ଓ - ନ */ - 0x0b2a, 0x0b30, /* ପ - ର */ - 0x0b32, 0x0b33, /* ଲ - ଳ */ - 0x0b36, 0x0b39, /* ଶ - ହ */ - 0x0b5c, 0x0b5d, /* ଡ଼ - ଢ଼ */ - 0x0b5f, 0x0b61, /* ୟ - ୡ */ - 0x0b85, 0x0b8a, /* அ - ஊ */ - 0x0b8e, 0x0b90, /* எ - ஐ */ - 0x0b92, 0x0b95, /* ஒ - க */ - 0x0b99, 0x0b9a, /* ங - ச */ - 0x0b9e, 0x0b9f, /* ஞ - ட */ - 0x0ba3, 0x0ba4, /* ண - த */ - 0x0ba8, 0x0baa, /* ந - ப */ - 0x0bae, 0x0bb5, /* ம - வ */ - 0x0bb7, 0x0bb9, /* ஷ - ஹ */ - 0x0c05, 0x0c0c, /* అ - ఌ */ - 0x0c0e, 0x0c10, /* ఎ - ఐ */ - 0x0c12, 0x0c28, /* ఒ - న */ - 0x0c2a, 0x0c33, /* ప - ళ */ - 0x0c35, 0x0c39, /* వ - హ */ - 0x0c60, 0x0c61, /* ౠ - ౡ */ - 0x0c85, 0x0c8c, /* ಅ - ಌ */ - 0x0c8e, 0x0c90, /* ಎ - ಐ */ - 0x0c92, 0x0ca8, /* ಒ - ನ */ - 0x0caa, 0x0cb3, /* ಪ - ಳ */ - 0x0cb5, 0x0cb9, /* ವ - ಹ */ - 0x0ce0, 0x0ce1, /* ೠ - ೡ */ - 0x0d05, 0x0d0c, /* അ - ഌ */ - 0x0d0e, 0x0d10, /* എ - ഐ */ - 0x0d12, 0x0d28, /* ഒ - ന */ - 0x0d2a, 0x0d39, /* പ - ഹ */ - 0x0d60, 0x0d61, /* ൠ - ൡ */ - 0x0e01, 0x0e30, /* ก - ะ */ - 0x0e32, 0x0e33, /* า - ำ */ - 0x0e40, 0x0e46, /* เ - ๆ */ - 0x0e5a, 0x0e5b, /* ๚ - ๛ */ - 0x0e81, 0x0e82, /* ກ - ຂ */ - 0x0e87, 0x0e88, /* ງ - ຈ */ - 0x0e94, 0x0e97, /* ດ - ທ */ - 0x0e99, 0x0e9f, /* ນ - ຟ */ - 0x0ea1, 0x0ea3, /* ມ - ຣ */ - 0x0eaa, 0x0eab, /* ສ - ຫ */ - 0x0ead, 0x0eae, /* ອ - ຮ */ - 0x0eb2, 0x0eb3, /* າ - ຳ */ - 0x0ec0, 0x0ec4, /* ເ - ໄ */ - 0x0edc, 0x0edd, /* ໜ - ໝ */ - 0x0f18, 0x0f19, /* ༘ - ༙ */ - 0x0f40, 0x0f47, /* ཀ - ཇ */ - 0x0f49, 0x0f69, /* ཉ - ཀྵ */ - 0x10d0, 0x10f6, /* ა - ჶ */ - 0x1100, 0x1159, /* ᄀ - ᅙ */ - 0x115f, 0x11a2, /* ᅟ - ᆢ */ - 0x11a8, 0x11f9, /* ᆨ - ᇹ */ - 0x1e00, 0x1e9b, /* Ḁ - ẛ */ - 0x1f50, 0x1f57, /* ὐ - ὗ */ - 0x1f80, 0x1fb4, /* ᾀ - ᾴ */ - 0x1fb6, 0x1fbc, /* ᾶ - ᾼ */ - 0x1fc2, 0x1fc4, /* ῂ - ῄ */ - 0x1fc6, 0x1fcc, /* ῆ - ῌ */ - 0x1fd0, 0x1fd3, /* ῐ - ΐ */ - 0x1fd6, 0x1fdb, /* ῖ - Ί */ - 0x1fe0, 0x1fec, /* ῠ - Ῥ */ - 0x1ff2, 0x1ff4, /* ῲ - ῴ */ - 0x1ff6, 0x1ffc, /* ῶ - ῼ */ - 0x210a, 0x2113, /* ℊ - ℓ */ - 0x2115, 0x211d, /* ℕ - ℝ */ - 0x2120, 0x2122, /* ℠ - ™ */ - 0x212a, 0x2131, /* K - ℱ */ - 0x2133, 0x2138, /* ℳ - ℸ */ - 0x3041, 0x3094, /* ぁ - ゔ */ - 0x30a1, 0x30fa, /* ァ - ヺ */ - 0x3105, 0x312c, /* ㄅ - ㄬ */ - 0x3131, 0x318e, /* ㄱ - ㆎ */ - 0x3192, 0x319f, /* ㆒ - ㆟ */ - 0x3260, 0x327b, /* ㉠ - ㉻ */ - 0x328a, 0x32b0, /* ㊊ - ㊰ */ - 0x32d0, 0x32fe, /* ㋐ - ㋾ */ - 0x3300, 0x3357, /* ㌀ - ㍗ */ - 0x3371, 0x3376, /* ㍱ - ㍶ */ - 0x337b, 0x3394, /* ㍻ - ㎔ */ - 0x3399, 0x339e, /* ㎙ - ㎞ */ - 0x33a9, 0x33ad, /* ㎩ - ㎭ */ - 0x33b0, 0x33c1, /* ㎰ - ㏁ */ - 0x33c3, 0x33c5, /* ㏃ - ㏅ */ - 0x33c7, 0x33d7, /* ㏇ - ㏗ */ - 0x33d9, 0x33dd, /* ㏙ - ㏝ */ - 0x4e00, 0x9fff, /* 一 - 鿿 */ - 0xac00, 0xd7a3, /* 가 - 힣 */ - 0xf900, 0xfb06, /* 豈 - st */ - 0xfb13, 0xfb17, /* ﬓ - ﬗ */ - 0xfb1f, 0xfb28, /* ײַ - ﬨ */ - 0xfb2a, 0xfb36, /* שׁ - זּ */ - 0xfb38, 0xfb3c, /* טּ - לּ */ - 0xfb40, 0xfb41, /* נּ - סּ */ - 0xfb43, 0xfb44, /* ףּ - פּ */ - 0xfb46, 0xfbb1, /* צּ - ﮱ */ - 0xfbd3, 0xfd3d, /* ﯓ - ﴽ */ - 0xfd50, 0xfd8f, /* ﵐ - ﶏ */ - 0xfd92, 0xfdc7, /* ﶒ - ﷇ */ - 0xfdf0, 0xfdf9, /* ﷰ - ﷹ */ - 0xfe70, 0xfe72, /* ﹰ - ﹲ */ - 0xfe76, 0xfefc, /* ﹶ - ﻼ */ - 0xff66, 0xff6f, /* ヲ - ッ */ - 0xff71, 0xff9d, /* ア - ン */ - 0xffa0, 0xffbe, /* ᅠ - ᄒ */ - 0xffc2, 0xffc7, /* ᅡ - ᅦ */ - 0xffca, 0xffcf, /* ᅧ - ᅬ */ - 0xffd2, 0xffd7, /* ᅭ - ᅲ */ - 0xffda, 0xffdc, /* ᅳ - ᅵ */ -}; - -/* - * alpha singlets - - * only covers ranges not in lower||upper - */ -static -Rune __alpha1[] = -{ - 0x00aa, /* ª */ - 0x00b5, /* µ */ - 0x00ba, /* º */ - 0x03da, /* Ϛ */ - 0x03dc, /* Ϝ */ - 0x03de, /* Ϟ */ - 0x03e0, /* Ϡ */ - 0x06d5, /* ە */ - 0x09b2, /* ল */ - 0x0a5e, /* ਫ਼ */ - 0x0a8d, /* ઍ */ - 0x0ae0, /* ૠ */ - 0x0b9c, /* ஜ */ - 0x0cde, /* ೞ */ - 0x0e4f, /* ๏ */ - 0x0e84, /* ຄ */ - 0x0e8a, /* ຊ */ - 0x0e8d, /* ຍ */ - 0x0ea5, /* ລ */ - 0x0ea7, /* ວ */ - 0x0eb0, /* ະ */ - 0x0ebd, /* ຽ */ - 0x1fbe, /* ι */ - 0x207f, /* ⁿ */ - 0x20a8, /* ₨ */ - 0x2102, /* ℂ */ - 0x2107, /* ℇ */ - 0x2124, /* ℤ */ - 0x2126, /* Ω */ - 0x2128, /* ℨ */ - 0xfb3e, /* מּ */ - 0xfe74, /* ﹴ */ -}; - -/* - * space ranges - */ -static -Rune __space2[] = -{ - 0x0009, 0x000a, /* tab and newline */ - 0x0020, 0x0020, /* space */ - 0x00a0, 0x00a0, /*   */ - 0x2000, 0x200b, /*   - ​ */ - 0x2028, 0x2029, /* 
 - 
 */ - 0x3000, 0x3000, /*   */ - 0xfeff, 0xfeff, /*  */ -}; - -/* - * lower case ranges - * 3rd col is conversion excess 500 - */ -static -Rune __toupper2[] = -{ - 0x0061, 0x007a, 468, /* a-z A-Z */ - 0x00e0, 0x00f6, 468, /* à-ö À-Ö */ - 0x00f8, 0x00fe, 468, /* ø-þ Ø-Þ */ - 0x0256, 0x0257, 295, /* ɖ-ɗ Ɖ-Ɗ */ - 0x0258, 0x0259, 298, /* ɘ-ə Ǝ-Ə */ - 0x028a, 0x028b, 283, /* ʊ-ʋ Ʊ-Ʋ */ - 0x03ad, 0x03af, 463, /* έ-ί Έ-Ί */ - 0x03b1, 0x03c1, 468, /* α-ρ Α-Ρ */ - 0x03c3, 0x03cb, 468, /* σ-ϋ Σ-Ϋ */ - 0x03cd, 0x03ce, 437, /* ύ-ώ Ύ-Ώ */ - 0x0430, 0x044f, 468, /* а-я А-Я */ - 0x0451, 0x045c, 420, /* ё-ќ Ё-Ќ */ - 0x045e, 0x045f, 420, /* ў-џ Ў-Џ */ - 0x0561, 0x0586, 452, /* ա-ֆ Ա-Ֆ */ - 0x1f00, 0x1f07, 508, /* ἀ-ἇ Ἀ-Ἇ */ - 0x1f10, 0x1f15, 508, /* ἐ-ἕ Ἐ-Ἕ */ - 0x1f20, 0x1f27, 508, /* ἠ-ἧ Ἠ-Ἧ */ - 0x1f30, 0x1f37, 508, /* ἰ-ἷ Ἰ-Ἷ */ - 0x1f40, 0x1f45, 508, /* ὀ-ὅ Ὀ-Ὅ */ - 0x1f60, 0x1f67, 508, /* ὠ-ὧ Ὠ-Ὧ */ - 0x1f70, 0x1f71, 574, /* ὰ-ά Ὰ-Ά */ - 0x1f72, 0x1f75, 586, /* ὲ-ή Ὲ-Ή */ - 0x1f76, 0x1f77, 600, /* ὶ-ί Ὶ-Ί */ - 0x1f78, 0x1f79, 628, /* ὸ-ό Ὸ-Ό */ - 0x1f7a, 0x1f7b, 612, /* ὺ-ύ Ὺ-Ύ */ - 0x1f7c, 0x1f7d, 626, /* ὼ-ώ Ὼ-Ώ */ - 0x1f80, 0x1f87, 508, /* ᾀ-ᾇ ᾈ-ᾏ */ - 0x1f90, 0x1f97, 508, /* ᾐ-ᾗ ᾘ-ᾟ */ - 0x1fa0, 0x1fa7, 508, /* ᾠ-ᾧ ᾨ-ᾯ */ - 0x1fb0, 0x1fb1, 508, /* ᾰ-ᾱ Ᾰ-Ᾱ */ - 0x1fd0, 0x1fd1, 508, /* ῐ-ῑ Ῐ-Ῑ */ - 0x1fe0, 0x1fe1, 508, /* ῠ-ῡ Ῠ-Ῡ */ - 0x2170, 0x217f, 484, /* ⅰ-ⅿ Ⅰ-Ⅿ */ - 0x24d0, 0x24e9, 474, /* ⓐ-ⓩ Ⓐ-Ⓩ */ - 0xff41, 0xff5a, 468, /* a-z A-Z */ -}; - -/* - * lower case singlets - * 2nd col is conversion excess 500 - */ -static -Rune __toupper1[] = -{ - 0x00ff, 621, /* ÿ Ÿ */ - 0x0101, 499, /* ā Ā */ - 0x0103, 499, /* ă Ă */ - 0x0105, 499, /* ą Ą */ - 0x0107, 499, /* ć Ć */ - 0x0109, 499, /* ĉ Ĉ */ - 0x010b, 499, /* ċ Ċ */ - 0x010d, 499, /* č Č */ - 0x010f, 499, /* ď Ď */ - 0x0111, 499, /* đ Đ */ - 0x0113, 499, /* ē Ē */ - 0x0115, 499, /* ĕ Ĕ */ - 0x0117, 499, /* ė Ė */ - 0x0119, 499, /* ę Ę */ - 0x011b, 499, /* ě Ě */ - 0x011d, 499, /* ĝ Ĝ */ - 0x011f, 499, /* ğ Ğ */ - 0x0121, 499, /* ġ Ġ */ - 0x0123, 499, /* ģ Ģ */ - 0x0125, 499, /* ĥ Ĥ */ - 0x0127, 499, /* ħ Ħ */ - 0x0129, 499, /* ĩ Ĩ */ - 0x012b, 499, /* ī Ī */ - 0x012d, 499, /* ĭ Ĭ */ - 0x012f, 499, /* į Į */ - 0x0131, 268, /* ı I */ - 0x0133, 499, /* ij IJ */ - 0x0135, 499, /* ĵ Ĵ */ - 0x0137, 499, /* ķ Ķ */ - 0x013a, 499, /* ĺ Ĺ */ - 0x013c, 499, /* ļ Ļ */ - 0x013e, 499, /* ľ Ľ */ - 0x0140, 499, /* ŀ Ŀ */ - 0x0142, 499, /* ł Ł */ - 0x0144, 499, /* ń Ń */ - 0x0146, 499, /* ņ Ņ */ - 0x0148, 499, /* ň Ň */ - 0x014b, 499, /* ŋ Ŋ */ - 0x014d, 499, /* ō Ō */ - 0x014f, 499, /* ŏ Ŏ */ - 0x0151, 499, /* ő Ő */ - 0x0153, 499, /* œ Œ */ - 0x0155, 499, /* ŕ Ŕ */ - 0x0157, 499, /* ŗ Ŗ */ - 0x0159, 499, /* ř Ř */ - 0x015b, 499, /* ś Ś */ - 0x015d, 499, /* ŝ Ŝ */ - 0x015f, 499, /* ş Ş */ - 0x0161, 499, /* š Š */ - 0x0163, 499, /* ţ Ţ */ - 0x0165, 499, /* ť Ť */ - 0x0167, 499, /* ŧ Ŧ */ - 0x0169, 499, /* ũ Ũ */ - 0x016b, 499, /* ū Ū */ - 0x016d, 499, /* ŭ Ŭ */ - 0x016f, 499, /* ů Ů */ - 0x0171, 499, /* ű Ű */ - 0x0173, 499, /* ų Ų */ - 0x0175, 499, /* ŵ Ŵ */ - 0x0177, 499, /* ŷ Ŷ */ - 0x017a, 499, /* ź Ź */ - 0x017c, 499, /* ż Ż */ - 0x017e, 499, /* ž Ž */ - 0x017f, 200, /* ſ S */ - 0x0183, 499, /* ƃ Ƃ */ - 0x0185, 499, /* ƅ Ƅ */ - 0x0188, 499, /* ƈ Ƈ */ - 0x018c, 499, /* ƌ Ƌ */ - 0x0192, 499, /* ƒ Ƒ */ - 0x0199, 499, /* ƙ Ƙ */ - 0x01a1, 499, /* ơ Ơ */ - 0x01a3, 499, /* ƣ Ƣ */ - 0x01a5, 499, /* ƥ Ƥ */ - 0x01a8, 499, /* ƨ Ƨ */ - 0x01ad, 499, /* ƭ Ƭ */ - 0x01b0, 499, /* ư Ư */ - 0x01b4, 499, /* ƴ Ƴ */ - 0x01b6, 499, /* ƶ Ƶ */ - 0x01b9, 499, /* ƹ Ƹ */ - 0x01bd, 499, /* ƽ Ƽ */ - 0x01c5, 499, /* Dž DŽ */ - 0x01c6, 498, /* dž DŽ */ - 0x01c8, 499, /* Lj LJ */ - 0x01c9, 498, /* lj LJ */ - 0x01cb, 499, /* Nj NJ */ - 0x01cc, 498, /* nj NJ */ - 0x01ce, 499, /* ǎ Ǎ */ - 0x01d0, 499, /* ǐ Ǐ */ - 0x01d2, 499, /* ǒ Ǒ */ - 0x01d4, 499, /* ǔ Ǔ */ - 0x01d6, 499, /* ǖ Ǖ */ - 0x01d8, 499, /* ǘ Ǘ */ - 0x01da, 499, /* ǚ Ǚ */ - 0x01dc, 499, /* ǜ Ǜ */ - 0x01df, 499, /* ǟ Ǟ */ - 0x01e1, 499, /* ǡ Ǡ */ - 0x01e3, 499, /* ǣ Ǣ */ - 0x01e5, 499, /* ǥ Ǥ */ - 0x01e7, 499, /* ǧ Ǧ */ - 0x01e9, 499, /* ǩ Ǩ */ - 0x01eb, 499, /* ǫ Ǫ */ - 0x01ed, 499, /* ǭ Ǭ */ - 0x01ef, 499, /* ǯ Ǯ */ - 0x01f2, 499, /* Dz DZ */ - 0x01f3, 498, /* dz DZ */ - 0x01f5, 499, /* ǵ Ǵ */ - 0x01fb, 499, /* ǻ Ǻ */ - 0x01fd, 499, /* ǽ Ǽ */ - 0x01ff, 499, /* ǿ Ǿ */ - 0x0201, 499, /* ȁ Ȁ */ - 0x0203, 499, /* ȃ Ȃ */ - 0x0205, 499, /* ȅ Ȅ */ - 0x0207, 499, /* ȇ Ȇ */ - 0x0209, 499, /* ȉ Ȉ */ - 0x020b, 499, /* ȋ Ȋ */ - 0x020d, 499, /* ȍ Ȍ */ - 0x020f, 499, /* ȏ Ȏ */ - 0x0211, 499, /* ȑ Ȑ */ - 0x0213, 499, /* ȓ Ȓ */ - 0x0215, 499, /* ȕ Ȕ */ - 0x0217, 499, /* ȗ Ȗ */ - 0x0253, 290, /* ɓ Ɓ */ - 0x0254, 294, /* ɔ Ɔ */ - 0x025b, 297, /* ɛ Ɛ */ - 0x0260, 295, /* ɠ Ɠ */ - 0x0263, 293, /* ɣ Ɣ */ - 0x0268, 291, /* ɨ Ɨ */ - 0x0269, 289, /* ɩ Ɩ */ - 0x026f, 289, /* ɯ Ɯ */ - 0x0272, 287, /* ɲ Ɲ */ - 0x0283, 282, /* ʃ Ʃ */ - 0x0288, 282, /* ʈ Ʈ */ - 0x0292, 281, /* ʒ Ʒ */ - 0x03ac, 462, /* ά Ά */ - 0x03cc, 436, /* ό Ό */ - 0x03d0, 438, /* ϐ Β */ - 0x03d1, 443, /* ϑ Θ */ - 0x03d5, 453, /* ϕ Φ */ - 0x03d6, 446, /* ϖ Π */ - 0x03e3, 499, /* ϣ Ϣ */ - 0x03e5, 499, /* ϥ Ϥ */ - 0x03e7, 499, /* ϧ Ϧ */ - 0x03e9, 499, /* ϩ Ϩ */ - 0x03eb, 499, /* ϫ Ϫ */ - 0x03ed, 499, /* ϭ Ϭ */ - 0x03ef, 499, /* ϯ Ϯ */ - 0x03f0, 414, /* ϰ Κ */ - 0x03f1, 420, /* ϱ Ρ */ - 0x0461, 499, /* ѡ Ѡ */ - 0x0463, 499, /* ѣ Ѣ */ - 0x0465, 499, /* ѥ Ѥ */ - 0x0467, 499, /* ѧ Ѧ */ - 0x0469, 499, /* ѩ Ѩ */ - 0x046b, 499, /* ѫ Ѫ */ - 0x046d, 499, /* ѭ Ѭ */ - 0x046f, 499, /* ѯ Ѯ */ - 0x0471, 499, /* ѱ Ѱ */ - 0x0473, 499, /* ѳ Ѳ */ - 0x0475, 499, /* ѵ Ѵ */ - 0x0477, 499, /* ѷ Ѷ */ - 0x0479, 499, /* ѹ Ѹ */ - 0x047b, 499, /* ѻ Ѻ */ - 0x047d, 499, /* ѽ Ѽ */ - 0x047f, 499, /* ѿ Ѿ */ - 0x0481, 499, /* ҁ Ҁ */ - 0x0491, 499, /* ґ Ґ */ - 0x0493, 499, /* ғ Ғ */ - 0x0495, 499, /* ҕ Ҕ */ - 0x0497, 499, /* җ Җ */ - 0x0499, 499, /* ҙ Ҙ */ - 0x049b, 499, /* қ Қ */ - 0x049d, 499, /* ҝ Ҝ */ - 0x049f, 499, /* ҟ Ҟ */ - 0x04a1, 499, /* ҡ Ҡ */ - 0x04a3, 499, /* ң Ң */ - 0x04a5, 499, /* ҥ Ҥ */ - 0x04a7, 499, /* ҧ Ҧ */ - 0x04a9, 499, /* ҩ Ҩ */ - 0x04ab, 499, /* ҫ Ҫ */ - 0x04ad, 499, /* ҭ Ҭ */ - 0x04af, 499, /* ү Ү */ - 0x04b1, 499, /* ұ Ұ */ - 0x04b3, 499, /* ҳ Ҳ */ - 0x04b5, 499, /* ҵ Ҵ */ - 0x04b7, 499, /* ҷ Ҷ */ - 0x04b9, 499, /* ҹ Ҹ */ - 0x04bb, 499, /* һ Һ */ - 0x04bd, 499, /* ҽ Ҽ */ - 0x04bf, 499, /* ҿ Ҿ */ - 0x04c2, 499, /* ӂ Ӂ */ - 0x04c4, 499, /* ӄ Ӄ */ - 0x04c8, 499, /* ӈ Ӈ */ - 0x04cc, 499, /* ӌ Ӌ */ - 0x04d1, 499, /* ӑ Ӑ */ - 0x04d3, 499, /* ӓ Ӓ */ - 0x04d5, 499, /* ӕ Ӕ */ - 0x04d7, 499, /* ӗ Ӗ */ - 0x04d9, 499, /* ә Ә */ - 0x04db, 499, /* ӛ Ӛ */ - 0x04dd, 499, /* ӝ Ӝ */ - 0x04df, 499, /* ӟ Ӟ */ - 0x04e1, 499, /* ӡ Ӡ */ - 0x04e3, 499, /* ӣ Ӣ */ - 0x04e5, 499, /* ӥ Ӥ */ - 0x04e7, 499, /* ӧ Ӧ */ - 0x04e9, 499, /* ө Ө */ - 0x04eb, 499, /* ӫ Ӫ */ - 0x04ef, 499, /* ӯ Ӯ */ - 0x04f1, 499, /* ӱ Ӱ */ - 0x04f3, 499, /* ӳ Ӳ */ - 0x04f5, 499, /* ӵ Ӵ */ - 0x04f9, 499, /* ӹ Ӹ */ - 0x1e01, 499, /* ḁ Ḁ */ - 0x1e03, 499, /* ḃ Ḃ */ - 0x1e05, 499, /* ḅ Ḅ */ - 0x1e07, 499, /* ḇ Ḇ */ - 0x1e09, 499, /* ḉ Ḉ */ - 0x1e0b, 499, /* ḋ Ḋ */ - 0x1e0d, 499, /* ḍ Ḍ */ - 0x1e0f, 499, /* ḏ Ḏ */ - 0x1e11, 499, /* ḑ Ḑ */ - 0x1e13, 499, /* ḓ Ḓ */ - 0x1e15, 499, /* ḕ Ḕ */ - 0x1e17, 499, /* ḗ Ḗ */ - 0x1e19, 499, /* ḙ Ḙ */ - 0x1e1b, 499, /* ḛ Ḛ */ - 0x1e1d, 499, /* ḝ Ḝ */ - 0x1e1f, 499, /* ḟ Ḟ */ - 0x1e21, 499, /* ḡ Ḡ */ - 0x1e23, 499, /* ḣ Ḣ */ - 0x1e25, 499, /* ḥ Ḥ */ - 0x1e27, 499, /* ḧ Ḧ */ - 0x1e29, 499, /* ḩ Ḩ */ - 0x1e2b, 499, /* ḫ Ḫ */ - 0x1e2d, 499, /* ḭ Ḭ */ - 0x1e2f, 499, /* ḯ Ḯ */ - 0x1e31, 499, /* ḱ Ḱ */ - 0x1e33, 499, /* ḳ Ḳ */ - 0x1e35, 499, /* ḵ Ḵ */ - 0x1e37, 499, /* ḷ Ḷ */ - 0x1e39, 499, /* ḹ Ḹ */ - 0x1e3b, 499, /* ḻ Ḻ */ - 0x1e3d, 499, /* ḽ Ḽ */ - 0x1e3f, 499, /* ḿ Ḿ */ - 0x1e41, 499, /* ṁ Ṁ */ - 0x1e43, 499, /* ṃ Ṃ */ - 0x1e45, 499, /* ṅ Ṅ */ - 0x1e47, 499, /* ṇ Ṇ */ - 0x1e49, 499, /* ṉ Ṉ */ - 0x1e4b, 499, /* ṋ Ṋ */ - 0x1e4d, 499, /* ṍ Ṍ */ - 0x1e4f, 499, /* ṏ Ṏ */ - 0x1e51, 499, /* ṑ Ṑ */ - 0x1e53, 499, /* ṓ Ṓ */ - 0x1e55, 499, /* ṕ Ṕ */ - 0x1e57, 499, /* ṗ Ṗ */ - 0x1e59, 499, /* ṙ Ṙ */ - 0x1e5b, 499, /* ṛ Ṛ */ - 0x1e5d, 499, /* ṝ Ṝ */ - 0x1e5f, 499, /* ṟ Ṟ */ - 0x1e61, 499, /* ṡ Ṡ */ - 0x1e63, 499, /* ṣ Ṣ */ - 0x1e65, 499, /* ṥ Ṥ */ - 0x1e67, 499, /* ṧ Ṧ */ - 0x1e69, 499, /* ṩ Ṩ */ - 0x1e6b, 499, /* ṫ Ṫ */ - 0x1e6d, 499, /* ṭ Ṭ */ - 0x1e6f, 499, /* ṯ Ṯ */ - 0x1e71, 499, /* ṱ Ṱ */ - 0x1e73, 499, /* ṳ Ṳ */ - 0x1e75, 499, /* ṵ Ṵ */ - 0x1e77, 499, /* ṷ Ṷ */ - 0x1e79, 499, /* ṹ Ṹ */ - 0x1e7b, 499, /* ṻ Ṻ */ - 0x1e7d, 499, /* ṽ Ṽ */ - 0x1e7f, 499, /* ṿ Ṿ */ - 0x1e81, 499, /* ẁ Ẁ */ - 0x1e83, 499, /* ẃ Ẃ */ - 0x1e85, 499, /* ẅ Ẅ */ - 0x1e87, 499, /* ẇ Ẇ */ - 0x1e89, 499, /* ẉ Ẉ */ - 0x1e8b, 499, /* ẋ Ẋ */ - 0x1e8d, 499, /* ẍ Ẍ */ - 0x1e8f, 499, /* ẏ Ẏ */ - 0x1e91, 499, /* ẑ Ẑ */ - 0x1e93, 499, /* ẓ Ẓ */ - 0x1e95, 499, /* ẕ Ẕ */ - 0x1ea1, 499, /* ạ Ạ */ - 0x1ea3, 499, /* ả Ả */ - 0x1ea5, 499, /* ấ Ấ */ - 0x1ea7, 499, /* ầ Ầ */ - 0x1ea9, 499, /* ẩ Ẩ */ - 0x1eab, 499, /* ẫ Ẫ */ - 0x1ead, 499, /* ậ Ậ */ - 0x1eaf, 499, /* ắ Ắ */ - 0x1eb1, 499, /* ằ Ằ */ - 0x1eb3, 499, /* ẳ Ẳ */ - 0x1eb5, 499, /* ẵ Ẵ */ - 0x1eb7, 499, /* ặ Ặ */ - 0x1eb9, 499, /* ẹ Ẹ */ - 0x1ebb, 499, /* ẻ Ẻ */ - 0x1ebd, 499, /* ẽ Ẽ */ - 0x1ebf, 499, /* ế Ế */ - 0x1ec1, 499, /* ề Ề */ - 0x1ec3, 499, /* ể Ể */ - 0x1ec5, 499, /* ễ Ễ */ - 0x1ec7, 499, /* ệ Ệ */ - 0x1ec9, 499, /* ỉ Ỉ */ - 0x1ecb, 499, /* ị Ị */ - 0x1ecd, 499, /* ọ Ọ */ - 0x1ecf, 499, /* ỏ Ỏ */ - 0x1ed1, 499, /* ố Ố */ - 0x1ed3, 499, /* ồ Ồ */ - 0x1ed5, 499, /* ổ Ổ */ - 0x1ed7, 499, /* ỗ Ỗ */ - 0x1ed9, 499, /* ộ Ộ */ - 0x1edb, 499, /* ớ Ớ */ - 0x1edd, 499, /* ờ Ờ */ - 0x1edf, 499, /* ở Ở */ - 0x1ee1, 499, /* ỡ Ỡ */ - 0x1ee3, 499, /* ợ Ợ */ - 0x1ee5, 499, /* ụ Ụ */ - 0x1ee7, 499, /* ủ Ủ */ - 0x1ee9, 499, /* ứ Ứ */ - 0x1eeb, 499, /* ừ Ừ */ - 0x1eed, 499, /* ử Ử */ - 0x1eef, 499, /* ữ Ữ */ - 0x1ef1, 499, /* ự Ự */ - 0x1ef3, 499, /* ỳ Ỳ */ - 0x1ef5, 499, /* ỵ Ỵ */ - 0x1ef7, 499, /* ỷ Ỷ */ - 0x1ef9, 499, /* ỹ Ỹ */ - 0x1f51, 508, /* ὑ Ὑ */ - 0x1f53, 508, /* ὓ Ὓ */ - 0x1f55, 508, /* ὕ Ὕ */ - 0x1f57, 508, /* ὗ Ὗ */ - 0x1fb3, 509, /* ᾳ ᾼ */ - 0x1fc3, 509, /* ῃ ῌ */ - 0x1fe5, 507, /* ῥ Ῥ */ - 0x1ff3, 509, /* ῳ ῼ */ -}; - -/* - * upper case ranges - * 3rd col is conversion excess 500 - */ -static -Rune __tolower2[] = -{ - 0x0041, 0x005a, 532, /* A-Z a-z */ - 0x00c0, 0x00d6, 532, /* À-Ö à-ö */ - 0x00d8, 0x00de, 532, /* Ø-Þ ø-þ */ - 0x0189, 0x018a, 705, /* Ɖ-Ɗ ɖ-ɗ */ - 0x018e, 0x018f, 702, /* Ǝ-Ə ɘ-ə */ - 0x01b1, 0x01b2, 717, /* Ʊ-Ʋ ʊ-ʋ */ - 0x0388, 0x038a, 537, /* Έ-Ί έ-ί */ - 0x038e, 0x038f, 563, /* Ύ-Ώ ύ-ώ */ - 0x0391, 0x03a1, 532, /* Α-Ρ α-ρ */ - 0x03a3, 0x03ab, 532, /* Σ-Ϋ σ-ϋ */ - 0x0401, 0x040c, 580, /* Ё-Ќ ё-ќ */ - 0x040e, 0x040f, 580, /* Ў-Џ ў-џ */ - 0x0410, 0x042f, 532, /* А-Я а-я */ - 0x0531, 0x0556, 548, /* Ա-Ֆ ա-ֆ */ - 0x10a0, 0x10c5, 548, /* Ⴀ-Ⴥ ა-ჵ */ - 0x1f08, 0x1f0f, 492, /* Ἀ-Ἇ ἀ-ἇ */ - 0x1f18, 0x1f1d, 492, /* Ἐ-Ἕ ἐ-ἕ */ - 0x1f28, 0x1f2f, 492, /* Ἠ-Ἧ ἠ-ἧ */ - 0x1f38, 0x1f3f, 492, /* Ἰ-Ἷ ἰ-ἷ */ - 0x1f48, 0x1f4d, 492, /* Ὀ-Ὅ ὀ-ὅ */ - 0x1f68, 0x1f6f, 492, /* Ὠ-Ὧ ὠ-ὧ */ - 0x1f88, 0x1f8f, 492, /* ᾈ-ᾏ ᾀ-ᾇ */ - 0x1f98, 0x1f9f, 492, /* ᾘ-ᾟ ᾐ-ᾗ */ - 0x1fa8, 0x1faf, 492, /* ᾨ-ᾯ ᾠ-ᾧ */ - 0x1fb8, 0x1fb9, 492, /* Ᾰ-Ᾱ ᾰ-ᾱ */ - 0x1fba, 0x1fbb, 426, /* Ὰ-Ά ὰ-ά */ - 0x1fc8, 0x1fcb, 414, /* Ὲ-Ή ὲ-ή */ - 0x1fd8, 0x1fd9, 492, /* Ῐ-Ῑ ῐ-ῑ */ - 0x1fda, 0x1fdb, 400, /* Ὶ-Ί ὶ-ί */ - 0x1fe8, 0x1fe9, 492, /* Ῠ-Ῡ ῠ-ῡ */ - 0x1fea, 0x1feb, 388, /* Ὺ-Ύ ὺ-ύ */ - 0x1ff8, 0x1ff9, 372, /* Ὸ-Ό ὸ-ό */ - 0x1ffa, 0x1ffb, 374, /* Ὼ-Ώ ὼ-ώ */ - 0x2160, 0x216f, 516, /* Ⅰ-Ⅿ ⅰ-ⅿ */ - 0x24b6, 0x24cf, 526, /* Ⓐ-Ⓩ ⓐ-ⓩ */ - 0xff21, 0xff3a, 532, /* A-Z a-z */ -}; - -/* - * upper case singlets - * 2nd col is conversion excess 500 - */ -static -Rune __tolower1[] = -{ - 0x0100, 501, /* Ā ā */ - 0x0102, 501, /* Ă ă */ - 0x0104, 501, /* Ą ą */ - 0x0106, 501, /* Ć ć */ - 0x0108, 501, /* Ĉ ĉ */ - 0x010a, 501, /* Ċ ċ */ - 0x010c, 501, /* Č č */ - 0x010e, 501, /* Ď ď */ - 0x0110, 501, /* Đ đ */ - 0x0112, 501, /* Ē ē */ - 0x0114, 501, /* Ĕ ĕ */ - 0x0116, 501, /* Ė ė */ - 0x0118, 501, /* Ę ę */ - 0x011a, 501, /* Ě ě */ - 0x011c, 501, /* Ĝ ĝ */ - 0x011e, 501, /* Ğ ğ */ - 0x0120, 501, /* Ġ ġ */ - 0x0122, 501, /* Ģ ģ */ - 0x0124, 501, /* Ĥ ĥ */ - 0x0126, 501, /* Ħ ħ */ - 0x0128, 501, /* Ĩ ĩ */ - 0x012a, 501, /* Ī ī */ - 0x012c, 501, /* Ĭ ĭ */ - 0x012e, 501, /* Į į */ - 0x0130, 301, /* İ i */ - 0x0132, 501, /* IJ ij */ - 0x0134, 501, /* Ĵ ĵ */ - 0x0136, 501, /* Ķ ķ */ - 0x0139, 501, /* Ĺ ĺ */ - 0x013b, 501, /* Ļ ļ */ - 0x013d, 501, /* Ľ ľ */ - 0x013f, 501, /* Ŀ ŀ */ - 0x0141, 501, /* Ł ł */ - 0x0143, 501, /* Ń ń */ - 0x0145, 501, /* Ņ ņ */ - 0x0147, 501, /* Ň ň */ - 0x014a, 501, /* Ŋ ŋ */ - 0x014c, 501, /* Ō ō */ - 0x014e, 501, /* Ŏ ŏ */ - 0x0150, 501, /* Ő ő */ - 0x0152, 501, /* Œ œ */ - 0x0154, 501, /* Ŕ ŕ */ - 0x0156, 501, /* Ŗ ŗ */ - 0x0158, 501, /* Ř ř */ - 0x015a, 501, /* Ś ś */ - 0x015c, 501, /* Ŝ ŝ */ - 0x015e, 501, /* Ş ş */ - 0x0160, 501, /* Š š */ - 0x0162, 501, /* Ţ ţ */ - 0x0164, 501, /* Ť ť */ - 0x0166, 501, /* Ŧ ŧ */ - 0x0168, 501, /* Ũ ũ */ - 0x016a, 501, /* Ū ū */ - 0x016c, 501, /* Ŭ ŭ */ - 0x016e, 501, /* Ů ů */ - 0x0170, 501, /* Ű ű */ - 0x0172, 501, /* Ų ų */ - 0x0174, 501, /* Ŵ ŵ */ - 0x0176, 501, /* Ŷ ŷ */ - 0x0178, 379, /* Ÿ ÿ */ - 0x0179, 501, /* Ź ź */ - 0x017b, 501, /* Ż ż */ - 0x017d, 501, /* Ž ž */ - 0x0181, 710, /* Ɓ ɓ */ - 0x0182, 501, /* Ƃ ƃ */ - 0x0184, 501, /* Ƅ ƅ */ - 0x0186, 706, /* Ɔ ɔ */ - 0x0187, 501, /* Ƈ ƈ */ - 0x018b, 501, /* Ƌ ƌ */ - 0x0190, 703, /* Ɛ ɛ */ - 0x0191, 501, /* Ƒ ƒ */ - 0x0193, 705, /* Ɠ ɠ */ - 0x0194, 707, /* Ɣ ɣ */ - 0x0196, 711, /* Ɩ ɩ */ - 0x0197, 709, /* Ɨ ɨ */ - 0x0198, 501, /* Ƙ ƙ */ - 0x019c, 711, /* Ɯ ɯ */ - 0x019d, 713, /* Ɲ ɲ */ - 0x01a0, 501, /* Ơ ơ */ - 0x01a2, 501, /* Ƣ ƣ */ - 0x01a4, 501, /* Ƥ ƥ */ - 0x01a7, 501, /* Ƨ ƨ */ - 0x01a9, 718, /* Ʃ ʃ */ - 0x01ac, 501, /* Ƭ ƭ */ - 0x01ae, 718, /* Ʈ ʈ */ - 0x01af, 501, /* Ư ư */ - 0x01b3, 501, /* Ƴ ƴ */ - 0x01b5, 501, /* Ƶ ƶ */ - 0x01b7, 719, /* Ʒ ʒ */ - 0x01b8, 501, /* Ƹ ƹ */ - 0x01bc, 501, /* Ƽ ƽ */ - 0x01c4, 502, /* DŽ dž */ - 0x01c5, 501, /* Dž dž */ - 0x01c7, 502, /* LJ lj */ - 0x01c8, 501, /* Lj lj */ - 0x01ca, 502, /* NJ nj */ - 0x01cb, 501, /* Nj nj */ - 0x01cd, 501, /* Ǎ ǎ */ - 0x01cf, 501, /* Ǐ ǐ */ - 0x01d1, 501, /* Ǒ ǒ */ - 0x01d3, 501, /* Ǔ ǔ */ - 0x01d5, 501, /* Ǖ ǖ */ - 0x01d7, 501, /* Ǘ ǘ */ - 0x01d9, 501, /* Ǚ ǚ */ - 0x01db, 501, /* Ǜ ǜ */ - 0x01de, 501, /* Ǟ ǟ */ - 0x01e0, 501, /* Ǡ ǡ */ - 0x01e2, 501, /* Ǣ ǣ */ - 0x01e4, 501, /* Ǥ ǥ */ - 0x01e6, 501, /* Ǧ ǧ */ - 0x01e8, 501, /* Ǩ ǩ */ - 0x01ea, 501, /* Ǫ ǫ */ - 0x01ec, 501, /* Ǭ ǭ */ - 0x01ee, 501, /* Ǯ ǯ */ - 0x01f1, 502, /* DZ dz */ - 0x01f2, 501, /* Dz dz */ - 0x01f4, 501, /* Ǵ ǵ */ - 0x01fa, 501, /* Ǻ ǻ */ - 0x01fc, 501, /* Ǽ ǽ */ - 0x01fe, 501, /* Ǿ ǿ */ - 0x0200, 501, /* Ȁ ȁ */ - 0x0202, 501, /* Ȃ ȃ */ - 0x0204, 501, /* Ȅ ȅ */ - 0x0206, 501, /* Ȇ ȇ */ - 0x0208, 501, /* Ȉ ȉ */ - 0x020a, 501, /* Ȋ ȋ */ - 0x020c, 501, /* Ȍ ȍ */ - 0x020e, 501, /* Ȏ ȏ */ - 0x0210, 501, /* Ȑ ȑ */ - 0x0212, 501, /* Ȓ ȓ */ - 0x0214, 501, /* Ȕ ȕ */ - 0x0216, 501, /* Ȗ ȗ */ - 0x0386, 538, /* Ά ά */ - 0x038c, 564, /* Ό ό */ - 0x03e2, 501, /* Ϣ ϣ */ - 0x03e4, 501, /* Ϥ ϥ */ - 0x03e6, 501, /* Ϧ ϧ */ - 0x03e8, 501, /* Ϩ ϩ */ - 0x03ea, 501, /* Ϫ ϫ */ - 0x03ec, 501, /* Ϭ ϭ */ - 0x03ee, 501, /* Ϯ ϯ */ - 0x0460, 501, /* Ѡ ѡ */ - 0x0462, 501, /* Ѣ ѣ */ - 0x0464, 501, /* Ѥ ѥ */ - 0x0466, 501, /* Ѧ ѧ */ - 0x0468, 501, /* Ѩ ѩ */ - 0x046a, 501, /* Ѫ ѫ */ - 0x046c, 501, /* Ѭ ѭ */ - 0x046e, 501, /* Ѯ ѯ */ - 0x0470, 501, /* Ѱ ѱ */ - 0x0472, 501, /* Ѳ ѳ */ - 0x0474, 501, /* Ѵ ѵ */ - 0x0476, 501, /* Ѷ ѷ */ - 0x0478, 501, /* Ѹ ѹ */ - 0x047a, 501, /* Ѻ ѻ */ - 0x047c, 501, /* Ѽ ѽ */ - 0x047e, 501, /* Ѿ ѿ */ - 0x0480, 501, /* Ҁ ҁ */ - 0x0490, 501, /* Ґ ґ */ - 0x0492, 501, /* Ғ ғ */ - 0x0494, 501, /* Ҕ ҕ */ - 0x0496, 501, /* Җ җ */ - 0x0498, 501, /* Ҙ ҙ */ - 0x049a, 501, /* Қ қ */ - 0x049c, 501, /* Ҝ ҝ */ - 0x049e, 501, /* Ҟ ҟ */ - 0x04a0, 501, /* Ҡ ҡ */ - 0x04a2, 501, /* Ң ң */ - 0x04a4, 501, /* Ҥ ҥ */ - 0x04a6, 501, /* Ҧ ҧ */ - 0x04a8, 501, /* Ҩ ҩ */ - 0x04aa, 501, /* Ҫ ҫ */ - 0x04ac, 501, /* Ҭ ҭ */ - 0x04ae, 501, /* Ү ү */ - 0x04b0, 501, /* Ұ ұ */ - 0x04b2, 501, /* Ҳ ҳ */ - 0x04b4, 501, /* Ҵ ҵ */ - 0x04b6, 501, /* Ҷ ҷ */ - 0x04b8, 501, /* Ҹ ҹ */ - 0x04ba, 501, /* Һ һ */ - 0x04bc, 501, /* Ҽ ҽ */ - 0x04be, 501, /* Ҿ ҿ */ - 0x04c1, 501, /* Ӂ ӂ */ - 0x04c3, 501, /* Ӄ ӄ */ - 0x04c7, 501, /* Ӈ ӈ */ - 0x04cb, 501, /* Ӌ ӌ */ - 0x04d0, 501, /* Ӑ ӑ */ - 0x04d2, 501, /* Ӓ ӓ */ - 0x04d4, 501, /* Ӕ ӕ */ - 0x04d6, 501, /* Ӗ ӗ */ - 0x04d8, 501, /* Ә ә */ - 0x04da, 501, /* Ӛ ӛ */ - 0x04dc, 501, /* Ӝ ӝ */ - 0x04de, 501, /* Ӟ ӟ */ - 0x04e0, 501, /* Ӡ ӡ */ - 0x04e2, 501, /* Ӣ ӣ */ - 0x04e4, 501, /* Ӥ ӥ */ - 0x04e6, 501, /* Ӧ ӧ */ - 0x04e8, 501, /* Ө ө */ - 0x04ea, 501, /* Ӫ ӫ */ - 0x04ee, 501, /* Ӯ ӯ */ - 0x04f0, 501, /* Ӱ ӱ */ - 0x04f2, 501, /* Ӳ ӳ */ - 0x04f4, 501, /* Ӵ ӵ */ - 0x04f8, 501, /* Ӹ ӹ */ - 0x1e00, 501, /* Ḁ ḁ */ - 0x1e02, 501, /* Ḃ ḃ */ - 0x1e04, 501, /* Ḅ ḅ */ - 0x1e06, 501, /* Ḇ ḇ */ - 0x1e08, 501, /* Ḉ ḉ */ - 0x1e0a, 501, /* Ḋ ḋ */ - 0x1e0c, 501, /* Ḍ ḍ */ - 0x1e0e, 501, /* Ḏ ḏ */ - 0x1e10, 501, /* Ḑ ḑ */ - 0x1e12, 501, /* Ḓ ḓ */ - 0x1e14, 501, /* Ḕ ḕ */ - 0x1e16, 501, /* Ḗ ḗ */ - 0x1e18, 501, /* Ḙ ḙ */ - 0x1e1a, 501, /* Ḛ ḛ */ - 0x1e1c, 501, /* Ḝ ḝ */ - 0x1e1e, 501, /* Ḟ ḟ */ - 0x1e20, 501, /* Ḡ ḡ */ - 0x1e22, 501, /* Ḣ ḣ */ - 0x1e24, 501, /* Ḥ ḥ */ - 0x1e26, 501, /* Ḧ ḧ */ - 0x1e28, 501, /* Ḩ ḩ */ - 0x1e2a, 501, /* Ḫ ḫ */ - 0x1e2c, 501, /* Ḭ ḭ */ - 0x1e2e, 501, /* Ḯ ḯ */ - 0x1e30, 501, /* Ḱ ḱ */ - 0x1e32, 501, /* Ḳ ḳ */ - 0x1e34, 501, /* Ḵ ḵ */ - 0x1e36, 501, /* Ḷ ḷ */ - 0x1e38, 501, /* Ḹ ḹ */ - 0x1e3a, 501, /* Ḻ ḻ */ - 0x1e3c, 501, /* Ḽ ḽ */ - 0x1e3e, 501, /* Ḿ ḿ */ - 0x1e40, 501, /* Ṁ ṁ */ - 0x1e42, 501, /* Ṃ ṃ */ - 0x1e44, 501, /* Ṅ ṅ */ - 0x1e46, 501, /* Ṇ ṇ */ - 0x1e48, 501, /* Ṉ ṉ */ - 0x1e4a, 501, /* Ṋ ṋ */ - 0x1e4c, 501, /* Ṍ ṍ */ - 0x1e4e, 501, /* Ṏ ṏ */ - 0x1e50, 501, /* Ṑ ṑ */ - 0x1e52, 501, /* Ṓ ṓ */ - 0x1e54, 501, /* Ṕ ṕ */ - 0x1e56, 501, /* Ṗ ṗ */ - 0x1e58, 501, /* Ṙ ṙ */ - 0x1e5a, 501, /* Ṛ ṛ */ - 0x1e5c, 501, /* Ṝ ṝ */ - 0x1e5e, 501, /* Ṟ ṟ */ - 0x1e60, 501, /* Ṡ ṡ */ - 0x1e62, 501, /* Ṣ ṣ */ - 0x1e64, 501, /* Ṥ ṥ */ - 0x1e66, 501, /* Ṧ ṧ */ - 0x1e68, 501, /* Ṩ ṩ */ - 0x1e6a, 501, /* Ṫ ṫ */ - 0x1e6c, 501, /* Ṭ ṭ */ - 0x1e6e, 501, /* Ṯ ṯ */ - 0x1e70, 501, /* Ṱ ṱ */ - 0x1e72, 501, /* Ṳ ṳ */ - 0x1e74, 501, /* Ṵ ṵ */ - 0x1e76, 501, /* Ṷ ṷ */ - 0x1e78, 501, /* Ṹ ṹ */ - 0x1e7a, 501, /* Ṻ ṻ */ - 0x1e7c, 501, /* Ṽ ṽ */ - 0x1e7e, 501, /* Ṿ ṿ */ - 0x1e80, 501, /* Ẁ ẁ */ - 0x1e82, 501, /* Ẃ ẃ */ - 0x1e84, 501, /* Ẅ ẅ */ - 0x1e86, 501, /* Ẇ ẇ */ - 0x1e88, 501, /* Ẉ ẉ */ - 0x1e8a, 501, /* Ẋ ẋ */ - 0x1e8c, 501, /* Ẍ ẍ */ - 0x1e8e, 501, /* Ẏ ẏ */ - 0x1e90, 501, /* Ẑ ẑ */ - 0x1e92, 501, /* Ẓ ẓ */ - 0x1e94, 501, /* Ẕ ẕ */ - 0x1ea0, 501, /* Ạ ạ */ - 0x1ea2, 501, /* Ả ả */ - 0x1ea4, 501, /* Ấ ấ */ - 0x1ea6, 501, /* Ầ ầ */ - 0x1ea8, 501, /* Ẩ ẩ */ - 0x1eaa, 501, /* Ẫ ẫ */ - 0x1eac, 501, /* Ậ ậ */ - 0x1eae, 501, /* Ắ ắ */ - 0x1eb0, 501, /* Ằ ằ */ - 0x1eb2, 501, /* Ẳ ẳ */ - 0x1eb4, 501, /* Ẵ ẵ */ - 0x1eb6, 501, /* Ặ ặ */ - 0x1eb8, 501, /* Ẹ ẹ */ - 0x1eba, 501, /* Ẻ ẻ */ - 0x1ebc, 501, /* Ẽ ẽ */ - 0x1ebe, 501, /* Ế ế */ - 0x1ec0, 501, /* Ề ề */ - 0x1ec2, 501, /* Ể ể */ - 0x1ec4, 501, /* Ễ ễ */ - 0x1ec6, 501, /* Ệ ệ */ - 0x1ec8, 501, /* Ỉ ỉ */ - 0x1eca, 501, /* Ị ị */ - 0x1ecc, 501, /* Ọ ọ */ - 0x1ece, 501, /* Ỏ ỏ */ - 0x1ed0, 501, /* Ố ố */ - 0x1ed2, 501, /* Ồ ồ */ - 0x1ed4, 501, /* Ổ ổ */ - 0x1ed6, 501, /* Ỗ ỗ */ - 0x1ed8, 501, /* Ộ ộ */ - 0x1eda, 501, /* Ớ ớ */ - 0x1edc, 501, /* Ờ ờ */ - 0x1ede, 501, /* Ở ở */ - 0x1ee0, 501, /* Ỡ ỡ */ - 0x1ee2, 501, /* Ợ ợ */ - 0x1ee4, 501, /* Ụ ụ */ - 0x1ee6, 501, /* Ủ ủ */ - 0x1ee8, 501, /* Ứ ứ */ - 0x1eea, 501, /* Ừ ừ */ - 0x1eec, 501, /* Ử ử */ - 0x1eee, 501, /* Ữ ữ */ - 0x1ef0, 501, /* Ự ự */ - 0x1ef2, 501, /* Ỳ ỳ */ - 0x1ef4, 501, /* Ỵ ỵ */ - 0x1ef6, 501, /* Ỷ ỷ */ - 0x1ef8, 501, /* Ỹ ỹ */ - 0x1f59, 492, /* Ὑ ὑ */ - 0x1f5b, 492, /* Ὓ ὓ */ - 0x1f5d, 492, /* Ὕ ὕ */ - 0x1f5f, 492, /* Ὗ ὗ */ - 0x1fbc, 491, /* ᾼ ᾳ */ - 0x1fcc, 491, /* ῌ ῃ */ - 0x1fec, 493, /* Ῥ ῥ */ - 0x1ffc, 491, /* ῼ ῳ */ -}; - -/* - * title characters are those between - * upper and lower case. ie DZ Dz dz - */ -static -Rune __totitle1[] = -{ - 0x01c4, 501, /* DŽ Dž */ - 0x01c6, 499, /* dž Dž */ - 0x01c7, 501, /* LJ Lj */ - 0x01c9, 499, /* lj Lj */ - 0x01ca, 501, /* NJ Nj */ - 0x01cc, 499, /* nj Nj */ - 0x01f1, 501, /* DZ Dz */ - 0x01f3, 499, /* dz Dz */ -}; - -static Rune* -bsearch(Rune c, Rune *t, int n, int ne) -{ - Rune *p; - int m; - - while(n > 1) { - m = n/2; - p = t + m*ne; - if(c >= p[0]) { - t = p; - n = n-m; - } else - n = m; - } - if(n && c >= t[0]) - return t; - return 0; -} - -Rune -tolowerrune(Rune c) -{ - Rune *p; - - p = bsearch(c, __tolower2, nelem(__tolower2)/3, 3); - if(p && c >= p[0] && c <= p[1]) - return c + p[2] - 500; - p = bsearch(c, __tolower1, nelem(__tolower1)/2, 2); - if(p && c == p[0]) - return c + p[1] - 500; - return c; -} - -Rune -toupperrune(Rune c) -{ - Rune *p; - - p = bsearch(c, __toupper2, nelem(__toupper2)/3, 3); - if(p && c >= p[0] && c <= p[1]) - return c + p[2] - 500; - p = bsearch(c, __toupper1, nelem(__toupper1)/2, 2); - if(p && c == p[0]) - return c + p[1] - 500; - return c; -} - -Rune -totitlerune(Rune c) -{ - Rune *p; - - p = bsearch(c, __totitle1, nelem(__totitle1)/2, 2); - if(p && c == p[0]) - return c + p[1] - 500; - return c; -} - -int -islowerrune(Rune c) -{ - Rune *p; - - p = bsearch(c, __toupper2, nelem(__toupper2)/3, 3); - if(p && c >= p[0] && c <= p[1]) - return 1; - p = bsearch(c, __toupper1, nelem(__toupper1)/2, 2); - if(p && c == p[0]) - return 1; - return 0; -} - -int -isupperrune(Rune c) -{ - Rune *p; - - p = bsearch(c, __tolower2, nelem(__tolower2)/3, 3); - if(p && c >= p[0] && c <= p[1]) - return 1; - p = bsearch(c, __tolower1, nelem(__tolower1)/2, 2); - if(p && c == p[0]) - return 1; - return 0; -} - -int -isalpharune(Rune c) -{ - Rune *p; - - if(isupperrune(c) || islowerrune(c)) - return 1; - p = bsearch(c, __alpha2, nelem(__alpha2)/2, 2); - if(p && c >= p[0] && c <= p[1]) - return 1; - p = bsearch(c, __alpha1, nelem(__alpha1), 1); - if(p && c == p[0]) - return 1; - return 0; -} - -int -istitlerune(Rune c) -{ - return isupperrune(c) && islowerrune(c); -} - -int -isspacerune(Rune c) -{ - Rune *p; - - p = bsearch(c, __space2, nelem(__space2)/2, 2); - if(p && c >= p[0] && c <= p[1]) - return 1; - return 0; -} diff --git a/src/xmutil/third_party/libutf/utf.h b/src/xmutil/third_party/libutf/utf.h deleted file mode 100644 index b3ff73068..000000000 --- a/src/xmutil/third_party/libutf/utf.h +++ /dev/null @@ -1,53 +0,0 @@ -#ifndef _UTF_H_ -#define _UTF_H_ 1 -#if defined(__cplusplus) -extern "C" { -#endif - -typedef unsigned int Rune; /* 32 bits */ - -enum { - UTFmax = 4, /* maximum bytes per rune */ - Runesync = 0x80, /* cannot represent part of a UTF sequence (<) */ - Runeself = 0x80, /* rune and UTF sequences are the same (<) */ - Runeerror = 0xFFFD, /* decoding error in UTF */ - Runemax = 0x10FFFF /* maximum rune value */ -}; - -/* Edit .+1,/^$/ | cfn $PLAN9/src/lib9/utf/?*.c | grep -v static |grep -v __ */ -int chartorune(Rune *rune, const char *str); -int fullrune(char *str, int n); -int isalpharune(Rune c); -int islowerrune(Rune c); -int isspacerune(Rune c); -int istitlerune(Rune c); -int isupperrune(Rune c); -int runelen(long c); -int runenlen(Rune *r, int nrune); -Rune *runestrcat(Rune *s1, Rune *s2); -Rune *runestrchr(Rune *s, Rune c); -int runestrcmp(Rune *s1, Rune *s2); -Rune *runestrcpy(Rune *s1, Rune *s2); -Rune *runestrdup(Rune *s); -Rune *runestrecpy(Rune *s1, Rune *es1, Rune *s2); -long runestrlen(Rune *s); -Rune *runestrncat(Rune *s1, Rune *s2, long n); -int runestrncmp(Rune *s1, Rune *s2, long n); -Rune *runestrncpy(Rune *s1, Rune *s2, long n); -Rune *runestrrchr(Rune *s, Rune c); -Rune *runestrstr(Rune *s1, Rune *s2); -int runetochar(char *str, Rune *rune); -Rune tolowerrune(Rune c); -Rune totitlerune(Rune c); -Rune toupperrune(Rune c); -char *utfecpy(char *to, char *e, char *from); -int utflen(char *s); -int utfnlen(char *s, long m); -char *utfrrune(char *s, long c); -char *utfrune(char *s, long c); -char *utfutf(char *s1, char *s2); - -#if defined(__cplusplus) -} -#endif -#endif diff --git a/src/xmutil/third_party/libutf/utfdef.h b/src/xmutil/third_party/libutf/utfdef.h deleted file mode 100644 index 7bf73e51b..000000000 --- a/src/xmutil/third_party/libutf/utfdef.h +++ /dev/null @@ -1,35 +0,0 @@ -/* - * compiler directive on Plan 9 - */ -#ifndef USED -#define USED(x) \ - if (x) \ - ; \ - else -#endif - -/* - * easiest way to make sure these are defined - */ -#define uchar _fmtuchar -#define ushort _fmtushort -#define uint _fmtuint -#define ulong _fmtulong -#define vlong _fmtvlong -#define uvlong _fmtuvlong -typedef unsigned char uchar; -typedef unsigned short ushort; -typedef unsigned int uint; -typedef unsigned long ulong; -typedef unsigned long long uvlong; -typedef long long vlong; - -/* - * nil cannot be ((void*)0) on ANSI C, - * because it is used for function pointers - */ -#undef nil -#define nil 0 - -#undef nelem -#define nelem ((void *)0) diff --git a/src/xmutil/third_party/libutf/utfecpy.c b/src/xmutil/third_party/libutf/utfecpy.c deleted file mode 100644 index bab8136c2..000000000 --- a/src/xmutil/third_party/libutf/utfecpy.c +++ /dev/null @@ -1,38 +0,0 @@ -/* - * The authors of this software are Rob Pike and Ken Thompson. - * Copyright (c) 2002 by Lucent Technologies. - * Permission to use, copy, modify, and distribute this software for any - * purpose without fee is hereby granted, provided that this entire notice - * is included in all copies of any software which is or includes a copy - * or modification of this software and in all copies of the supporting - * documentation for such software. - * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED - * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE - * ANY REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY - * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. - */ -#define _BSD_SOURCE 1 /* memccpy */ -#define _DEFAULT_SOURCE 1 -#include -#include -#include "plan9.h" -#include "utf.h" - -char* -utfecpy(char *to, char *e, char *from) -{ - char *end; - - if(to >= e) - return to; - end = memccpy(to, from, '\0', e - to); - if(end == nil){ - end = e-1; - while(end>to && (*--end&0xC0)==0x80) - ; - *end = '\0'; - }else{ - end--; - } - return end; -} diff --git a/src/xmutil/third_party/libutf/utflen.c b/src/xmutil/third_party/libutf/utflen.c deleted file mode 100644 index 769805a5a..000000000 --- a/src/xmutil/third_party/libutf/utflen.c +++ /dev/null @@ -1,37 +0,0 @@ -/* - * The authors of this software are Rob Pike and Ken Thompson. - * Copyright (c) 2002 by Lucent Technologies. - * Permission to use, copy, modify, and distribute this software for any - * purpose without fee is hereby granted, provided that this entire notice - * is included in all copies of any software which is or includes a copy - * or modification of this software and in all copies of the supporting - * documentation for such software. - * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED - * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE - * ANY REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY - * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. - */ -#include -#include -#include "plan9.h" -#include "utf.h" - -int -utflen(char *s) -{ - int c; - long n; - Rune rune; - - n = 0; - for(;;) { - c = *(uchar*)s; - if(c < Runeself) { - if(c == 0) - return n; - s++; - } else - s += chartorune(&rune, s); - n++; - } -} diff --git a/src/xmutil/third_party/libutf/utfnlen.c b/src/xmutil/third_party/libutf/utfnlen.c deleted file mode 100644 index 668032995..000000000 --- a/src/xmutil/third_party/libutf/utfnlen.c +++ /dev/null @@ -1,41 +0,0 @@ -/* - * The authors of this software are Rob Pike and Ken Thompson. - * Copyright (c) 2002 by Lucent Technologies. - * Permission to use, copy, modify, and distribute this software for any - * purpose without fee is hereby granted, provided that this entire notice - * is included in all copies of any software which is or includes a copy - * or modification of this software and in all copies of the supporting - * documentation for such software. - * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED - * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE - * ANY REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY - * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. - */ -#include -#include -#include "plan9.h" -#include "utf.h" - -int -utfnlen(char *s, long m) -{ - int c; - long n; - Rune rune; - char *es; - - es = s + m; - for(n = 0; s < es; n++) { - c = *(uchar*)s; - if(c < Runeself){ - if(c == '\0') - break; - s++; - continue; - } - if(!fullrune(s, es-s)) - break; - s += chartorune(&rune, s); - } - return n; -} diff --git a/src/xmutil/third_party/libutf/utfrrune.c b/src/xmutil/third_party/libutf/utfrrune.c deleted file mode 100644 index cff12b5e2..000000000 --- a/src/xmutil/third_party/libutf/utfrrune.c +++ /dev/null @@ -1,45 +0,0 @@ -/* - * The authors of this software are Rob Pike and Ken Thompson. - * Copyright (c) 2002 by Lucent Technologies. - * Permission to use, copy, modify, and distribute this software for any - * purpose without fee is hereby granted, provided that this entire notice - * is included in all copies of any software which is or includes a copy - * or modification of this software and in all copies of the supporting - * documentation for such software. - * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED - * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE - * ANY REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY - * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. - */ -#include -#include -#include "plan9.h" -#include "utf.h" - -char* -utfrrune(char *s, long c) -{ - long c1; - Rune r; - char *s1; - - if(c < Runesync) /* not part of utf sequence */ - return strrchr(s, c); - - s1 = 0; - for(;;) { - c1 = *(uchar*)s; - if(c1 < Runeself) { /* one byte rune */ - if(c1 == 0) - return s1; - if(c1 == c) - s1 = s; - s++; - continue; - } - c1 = chartorune(&r, s); - if(r == c) - s1 = s; - s += c1; - } -} diff --git a/src/xmutil/third_party/libutf/utfrune.c b/src/xmutil/third_party/libutf/utfrune.c deleted file mode 100644 index 52b83599e..000000000 --- a/src/xmutil/third_party/libutf/utfrune.c +++ /dev/null @@ -1,44 +0,0 @@ -/* - * The authors of this software are Rob Pike and Ken Thompson. - * Copyright (c) 2002 by Lucent Technologies. - * Permission to use, copy, modify, and distribute this software for any - * purpose without fee is hereby granted, provided that this entire notice - * is included in all copies of any software which is or includes a copy - * or modification of this software and in all copies of the supporting - * documentation for such software. - * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED - * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE - * ANY REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY - * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. - */ -#include -#include -#include "plan9.h" -#include "utf.h" - -char* -utfrune(char *s, long c) -{ - long c1; - Rune r; - int n; - - if(c < Runesync) /* not part of utf sequence */ - return strchr(s, c); - - for(;;) { - c1 = *(uchar*)s; - if(c1 < Runeself) { /* one byte rune */ - if(c1 == 0) - return 0; - if(c1 == c) - return s; - s++; - continue; - } - n = chartorune(&r, s); - if(r == c) - return s; - s += n; - } -} diff --git a/src/xmutil/third_party/libutf/utfutf.c b/src/xmutil/third_party/libutf/utfutf.c deleted file mode 100644 index ffaff7089..000000000 --- a/src/xmutil/third_party/libutf/utfutf.c +++ /dev/null @@ -1,41 +0,0 @@ -/* - * The authors of this software are Rob Pike and Ken Thompson. - * Copyright (c) 2002 by Lucent Technologies. - * Permission to use, copy, modify, and distribute this software for any - * purpose without fee is hereby granted, provided that this entire notice - * is included in all copies of any software which is or includes a copy - * or modification of this software and in all copies of the supporting - * documentation for such software. - * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED - * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE - * ANY REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY - * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. - */ -#include -#include -#include "plan9.h" -#include "utf.h" - - -/* - * Return pointer to first occurrence of s2 in s1, - * 0 if none - */ -char* -utfutf(char *s1, char *s2) -{ - char *p; - long f, n1, n2; - Rune r; - - n1 = chartorune(&r, s2); - f = r; - if(f <= Runesync) /* represents self */ - return strstr(s1, s2); - - n2 = strlen(s2); - for(p=s1; (p=utfrune(p, f)); p+=n1) - if(strncmp(p, s2, n2) == 0) - return p; - return 0; -} diff --git a/src/xmutil/third_party/tinyxml2/LICENSE b/src/xmutil/third_party/tinyxml2/LICENSE deleted file mode 100644 index 85a6a36f0..000000000 --- a/src/xmutil/third_party/tinyxml2/LICENSE +++ /dev/null @@ -1,18 +0,0 @@ -This software is provided 'as-is', without any express or implied -warranty. In no event will the authors be held liable for any -damages arising from the use of this software. - -Permission is granted to anyone to use this software for any -purpose, including commercial applications, and to alter it and -redistribute it freely, subject to the following restrictions: - -1. The origin of this software must not be misrepresented; you must -not claim that you wrote the original software. If you use this -software in a product, an acknowledgment in the product documentation -would be appreciated but is not required. - -2. Altered source versions must be plainly marked as such, and -must not be misrepresented as being the original software. - -3. This notice may not be removed or altered from any source -distribution. diff --git a/src/xmutil/third_party/tinyxml2/tinyxml2.cpp b/src/xmutil/third_party/tinyxml2/tinyxml2.cpp deleted file mode 100755 index 8bb963582..000000000 --- a/src/xmutil/third_party/tinyxml2/tinyxml2.cpp +++ /dev/null @@ -1,3019 +0,0 @@ -/* -Original code by Lee Thomason (www.grinninglizard.com) - -This software is provided 'as-is', without any express or implied -warranty. In no event will the authors be held liable for any -damages arising from the use of this software. - -Permission is granted to anyone to use this software for any -purpose, including commercial applications, and to alter it and -redistribute it freely, subject to the following restrictions: - -1. The origin of this software must not be misrepresented; you must -not claim that you wrote the original software. If you use this -software in a product, an acknowledgment in the product documentation -would be appreciated but is not required. - -2. Altered source versions must be plainly marked as such, and -must not be misrepresented as being the original software. - -3. This notice may not be removed or altered from any source -distribution. -*/ - -#include "tinyxml2.h" - -#include // yes, this one new style header, is in the Android SDK. -#if defined(ANDROID_NDK) || defined(__BORLANDC__) || defined(__QNXNTO__) || defined(__CC_ARM) -# include -# include -#else -# include -# include -#endif - -#if defined(_MSC_VER) && (_MSC_VER >= 1400 ) && (!defined WINCE) - // Microsoft Visual Studio, version 2005 and higher. Not WinCE. - /*int _snprintf_s( - char *buffer, - size_t sizeOfBuffer, - size_t count, - const char *format [, - argument] ... - );*/ - static inline int TIXML_SNPRINTF( char* buffer, size_t size, const char* format, ... ) - { - va_list va; - va_start( va, format ); - const int result = vsnprintf_s( buffer, size, _TRUNCATE, format, va ); - va_end( va ); - return result; - } - - static inline int TIXML_VSNPRINTF( char* buffer, size_t size, const char* format, va_list va ) - { - const int result = vsnprintf_s( buffer, size, _TRUNCATE, format, va ); - return result; - } - - #define TIXML_VSCPRINTF _vscprintf - #define TIXML_SSCANF sscanf_s -#elif defined _MSC_VER - // Microsoft Visual Studio 2003 and earlier or WinCE - #define TIXML_SNPRINTF _snprintf - #define TIXML_VSNPRINTF _vsnprintf - #define TIXML_SSCANF sscanf - #if (_MSC_VER < 1400 ) && (!defined WINCE) - // Microsoft Visual Studio 2003 and not WinCE. - #define TIXML_VSCPRINTF _vscprintf // VS2003's C runtime has this, but VC6 C runtime or WinCE SDK doesn't have. - #else - // Microsoft Visual Studio 2003 and earlier or WinCE. - static inline int TIXML_VSCPRINTF( const char* format, va_list va ) - { - int len = 512; - for (;;) { - len = len*2; - char* str = new char[len](); - const int required = _vsnprintf(str, len, format, va); - delete[] str; - if ( required != -1 ) { - TIXMLASSERT( required >= 0 ); - len = required; - break; - } - } - TIXMLASSERT( len >= 0 ); - return len; - } - #endif -#else - // GCC version 3 and higher - //#warning( "Using sn* functions." ) - #define TIXML_SNPRINTF snprintf - #define TIXML_VSNPRINTF vsnprintf - static inline int TIXML_VSCPRINTF( const char* format, va_list va ) - { - int len = vsnprintf( 0, 0, format, va ); - TIXMLASSERT( len >= 0 ); - return len; - } - #define TIXML_SSCANF sscanf -#endif - -#if defined(_WIN64) - #define TIXML_FSEEK _fseeki64 - #define TIXML_FTELL _ftelli64 -#elif defined(__APPLE__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(__DragonFly__) || defined(__CYGWIN__) - #define TIXML_FSEEK fseeko - #define TIXML_FTELL ftello -#elif defined(__ANDROID__) && __ANDROID_API__ > 24 - #define TIXML_FSEEK fseeko64 - #define TIXML_FTELL ftello64 -#else - #define TIXML_FSEEK fseek - #define TIXML_FTELL ftell -#endif - - -static const char LINE_FEED = static_cast(0x0a); // all line endings are normalized to LF -static const char LF = LINE_FEED; -static const char CARRIAGE_RETURN = static_cast(0x0d); // CR gets filtered out -static const char CR = CARRIAGE_RETURN; -static const char SINGLE_QUOTE = '\''; -static const char DOUBLE_QUOTE = '\"'; - -// Bunch of unicode info at: -// http://www.unicode.org/faq/utf_bom.html -// ef bb bf (Microsoft "lead bytes") - designates UTF-8 - -static const unsigned char TIXML_UTF_LEAD_0 = 0xefU; -static const unsigned char TIXML_UTF_LEAD_1 = 0xbbU; -static const unsigned char TIXML_UTF_LEAD_2 = 0xbfU; - -namespace tinyxml2 -{ - -struct Entity { - const char* pattern; - int length; - char value; -}; - -static const int NUM_ENTITIES = 5; -static const Entity entities[NUM_ENTITIES] = { - { "quot", 4, DOUBLE_QUOTE }, - { "amp", 3, '&' }, - { "apos", 4, SINGLE_QUOTE }, - { "lt", 2, '<' }, - { "gt", 2, '>' } -}; - - -StrPair::~StrPair() -{ - Reset(); -} - - -void StrPair::TransferTo( StrPair* other ) -{ - if ( this == other ) { - return; - } - // This in effect implements the assignment operator by "moving" - // ownership (as in auto_ptr). - - TIXMLASSERT( other != 0 ); - TIXMLASSERT( other->_flags == 0 ); - TIXMLASSERT( other->_start == 0 ); - TIXMLASSERT( other->_end == 0 ); - - other->Reset(); - - other->_flags = _flags; - other->_start = _start; - other->_end = _end; - - _flags = 0; - _start = 0; - _end = 0; -} - - -void StrPair::Reset() -{ - if ( _flags & NEEDS_DELETE ) { - delete [] _start; - } - _flags = 0; - _start = 0; - _end = 0; -} - - -void StrPair::SetStr( const char* str, int flags ) -{ - TIXMLASSERT( str ); - Reset(); - size_t len = strlen( str ); - TIXMLASSERT( _start == 0 ); - _start = new char[ len+1 ]; - memcpy( _start, str, len+1 ); - _end = _start + len; - _flags = flags | NEEDS_DELETE; -} - - -char* StrPair::ParseText( char* p, const char* endTag, int strFlags, int* curLineNumPtr ) -{ - TIXMLASSERT( p ); - TIXMLASSERT( endTag && *endTag ); - TIXMLASSERT(curLineNumPtr); - - char* start = p; - const char endChar = *endTag; - size_t length = strlen( endTag ); - - // Inner loop of text parsing. - while ( *p ) { - if ( *p == endChar && strncmp( p, endTag, length ) == 0 ) { - Set( start, p, strFlags ); - return p + length; - } else if (*p == '\n') { - ++(*curLineNumPtr); - } - ++p; - TIXMLASSERT( p ); - } - return 0; -} - - -char* StrPair::ParseName( char* p ) -{ - if ( !p || !(*p) ) { - return 0; - } - if ( !XMLUtil::IsNameStartChar( static_cast(*p) ) ) { - return 0; - } - - char* const start = p; - ++p; - while ( *p && XMLUtil::IsNameChar( static_cast(*p) ) ) { - ++p; - } - - Set( start, p, 0 ); - return p; -} - - -void StrPair::CollapseWhitespace() -{ - // Adjusting _start would cause undefined behavior on delete[] - TIXMLASSERT( ( _flags & NEEDS_DELETE ) == 0 ); - // Trim leading space. - _start = XMLUtil::SkipWhiteSpace( _start, 0 ); - - if ( *_start ) { - const char* p = _start; // the read pointer - char* q = _start; // the write pointer - - while( *p ) { - if ( XMLUtil::IsWhiteSpace( *p )) { - p = XMLUtil::SkipWhiteSpace( p, 0 ); - if ( *p == 0 ) { - break; // don't write to q; this trims the trailing space. - } - *q = ' '; - ++q; - } - *q = *p; - ++q; - ++p; - } - *q = 0; - } -} - - -const char* StrPair::GetStr() -{ - TIXMLASSERT( _start ); - TIXMLASSERT( _end ); - if ( _flags & NEEDS_FLUSH ) { - *_end = 0; - _flags ^= NEEDS_FLUSH; - - if ( _flags ) { - const char* p = _start; // the read pointer - char* q = _start; // the write pointer - - while( p < _end ) { - if ( (_flags & NEEDS_NEWLINE_NORMALIZATION) && *p == CR ) { - // CR-LF pair becomes LF - // CR alone becomes LF - // LF-CR becomes LF - if ( *(p+1) == LF ) { - p += 2; - } - else { - ++p; - } - *q = LF; - ++q; - } - else if ( (_flags & NEEDS_NEWLINE_NORMALIZATION) && *p == LF ) { - if ( *(p+1) == CR ) { - p += 2; - } - else { - ++p; - } - *q = LF; - ++q; - } - else if ( (_flags & NEEDS_ENTITY_PROCESSING) && *p == '&' ) { - // Entities handled by tinyXML2: - // - special entities in the entity table [in/out] - // - numeric character reference [in] - // 中 or 中 - - if ( *(p+1) == '#' ) { - const int buflen = 10; - char buf[buflen] = { 0 }; - int len = 0; - const char* adjusted = const_cast( XMLUtil::GetCharacterRef( p, buf, &len ) ); - if ( adjusted == 0 ) { - *q = *p; - ++p; - ++q; - } - else { - TIXMLASSERT( 0 <= len && len <= buflen ); - TIXMLASSERT( q + len <= adjusted ); - p = adjusted; - memcpy( q, buf, len ); - q += len; - } - } - else { - bool entityFound = false; - for( int i = 0; i < NUM_ENTITIES; ++i ) { - const Entity& entity = entities[i]; - if ( strncmp( p + 1, entity.pattern, entity.length ) == 0 - && *( p + entity.length + 1 ) == ';' ) { - // Found an entity - convert. - *q = entity.value; - ++q; - p += entity.length + 2; - entityFound = true; - break; - } - } - if ( !entityFound ) { - // fixme: treat as error? - ++p; - ++q; - } - } - } - else { - *q = *p; - ++p; - ++q; - } - } - *q = 0; - } - // The loop below has plenty going on, and this - // is a less useful mode. Break it out. - if ( _flags & NEEDS_WHITESPACE_COLLAPSING ) { - CollapseWhitespace(); - } - _flags = (_flags & NEEDS_DELETE); - } - TIXMLASSERT( _start ); - return _start; -} - - - - -// --------- XMLUtil ----------- // - -const char* XMLUtil::writeBoolTrue = "true"; -const char* XMLUtil::writeBoolFalse = "false"; - -void XMLUtil::SetBoolSerialization(const char* writeTrue, const char* writeFalse) -{ - static const char* defTrue = "true"; - static const char* defFalse = "false"; - - writeBoolTrue = (writeTrue) ? writeTrue : defTrue; - writeBoolFalse = (writeFalse) ? writeFalse : defFalse; -} - - -const char* XMLUtil::ReadBOM( const char* p, bool* bom ) -{ - TIXMLASSERT( p ); - TIXMLASSERT( bom ); - *bom = false; - const unsigned char* pu = reinterpret_cast(p); - // Check for BOM: - if ( *(pu+0) == TIXML_UTF_LEAD_0 - && *(pu+1) == TIXML_UTF_LEAD_1 - && *(pu+2) == TIXML_UTF_LEAD_2 ) { - *bom = true; - p += 3; - } - TIXMLASSERT( p ); - return p; -} - - -void XMLUtil::ConvertUTF32ToUTF8( unsigned long input, char* output, int* length ) -{ - const unsigned long BYTE_MASK = 0xBF; - const unsigned long BYTE_MARK = 0x80; - const unsigned long FIRST_BYTE_MARK[7] = { 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC }; - - if (input < 0x80) { - *length = 1; - } - else if ( input < 0x800 ) { - *length = 2; - } - else if ( input < 0x10000 ) { - *length = 3; - } - else if ( input < 0x200000 ) { - *length = 4; - } - else { - *length = 0; // This code won't convert this correctly anyway. - return; - } - - output += *length; - - // Scary scary fall throughs are annotated with carefully designed comments - // to suppress compiler warnings such as -Wimplicit-fallthrough in gcc - switch (*length) { - case 4: - --output; - *output = static_cast((input | BYTE_MARK) & BYTE_MASK); - input >>= 6; - //fall through - case 3: - --output; - *output = static_cast((input | BYTE_MARK) & BYTE_MASK); - input >>= 6; - //fall through - case 2: - --output; - *output = static_cast((input | BYTE_MARK) & BYTE_MASK); - input >>= 6; - //fall through - case 1: - --output; - *output = static_cast(input | FIRST_BYTE_MARK[*length]); - break; - default: - TIXMLASSERT( false ); - } -} - - -const char* XMLUtil::GetCharacterRef(const char* p, char* value, int* length) -{ - // Assume an entity, and pull it out. - *length = 0; - - static const uint32_t MAX_CODE_POINT = 0x10FFFF; - - if (*(p + 1) == '#' && *(p + 2)) { - uint32_t ucs = 0; - ptrdiff_t delta = 0; - uint32_t mult = 1; - static const char SEMICOLON = ';'; - - bool hex = false; - uint32_t radix = 10; - const char* q = 0; - char terminator = '#'; - - if (*(p + 2) == 'x') { - // Hexadecimal. - hex = true; - radix = 16; - terminator = 'x'; - - q = p + 3; - } - else { - // Decimal. - q = p + 2; - } - if (!(*q)) { - return 0; - } - - q = strchr(q, SEMICOLON); - if (!q) { - return 0; - } - TIXMLASSERT(*q == SEMICOLON); - - delta = q - p; - --q; - - while (*q != terminator) { - uint32_t digit = 0; - - if (*q >= '0' && *q <= '9') { - digit = *q - '0'; - } - else if (hex && (*q >= 'a' && *q <= 'f')) { - digit = *q - 'a' + 10; - } - else if (hex && (*q >= 'A' && *q <= 'F')) { - digit = *q - 'A' + 10; - } - else { - return 0; - } - TIXMLASSERT(digit < radix); - - const unsigned int digitScaled = mult * digit; - ucs += digitScaled; - mult *= radix; - - // Security check: could a value exist that is out of range? - // Easily; limit to the MAX_CODE_POINT, which also allows for a - // bunch of leading zeroes. - if (mult > MAX_CODE_POINT) { - mult = MAX_CODE_POINT; - } - --q; - } - // Out of range: - if (ucs > MAX_CODE_POINT) { - return 0; - } - // convert the UCS to UTF-8 - ConvertUTF32ToUTF8(ucs, value, length); - if (length == 0) { - // If length is 0, there was an error. (Security? Bad input?) - // Fail safely. - return 0; - } - return p + delta + 1; - } - return p + 1; -} - -void XMLUtil::ToStr( int v, char* buffer, int bufferSize ) -{ - TIXML_SNPRINTF( buffer, bufferSize, "%d", v ); -} - - -void XMLUtil::ToStr( unsigned v, char* buffer, int bufferSize ) -{ - TIXML_SNPRINTF( buffer, bufferSize, "%u", v ); -} - - -void XMLUtil::ToStr( bool v, char* buffer, int bufferSize ) -{ - TIXML_SNPRINTF( buffer, bufferSize, "%s", v ? writeBoolTrue : writeBoolFalse); -} - -/* - ToStr() of a number is a very tricky topic. - https://github.com/leethomason/tinyxml2/issues/106 -*/ -void XMLUtil::ToStr( float v, char* buffer, int bufferSize ) -{ - TIXML_SNPRINTF( buffer, bufferSize, "%.8g", v ); -} - - -void XMLUtil::ToStr( double v, char* buffer, int bufferSize ) -{ - TIXML_SNPRINTF( buffer, bufferSize, "%.17g", v ); -} - - -void XMLUtil::ToStr( int64_t v, char* buffer, int bufferSize ) -{ - // horrible syntax trick to make the compiler happy about %lld - TIXML_SNPRINTF(buffer, bufferSize, "%lld", static_cast(v)); -} - -void XMLUtil::ToStr( uint64_t v, char* buffer, int bufferSize ) -{ - // horrible syntax trick to make the compiler happy about %llu - TIXML_SNPRINTF(buffer, bufferSize, "%llu", static_cast(v)); -} - -bool XMLUtil::ToInt(const char* str, int* value) -{ - if (IsPrefixHex(str)) { - unsigned v; - if (TIXML_SSCANF(str, "%x", &v) == 1) { - *value = static_cast(v); - return true; - } - } - else { - if (TIXML_SSCANF(str, "%d", value) == 1) { - return true; - } - } - return false; -} - -bool XMLUtil::ToUnsigned(const char* str, unsigned* value) -{ - if (TIXML_SSCANF(str, IsPrefixHex(str) ? "%x" : "%u", value) == 1) { - return true; - } - return false; -} - -bool XMLUtil::ToBool( const char* str, bool* value ) -{ - int ival = 0; - if ( ToInt( str, &ival )) { - *value = (ival==0) ? false : true; - return true; - } - static const char* TRUE_VALS[] = { "true", "True", "TRUE", 0 }; - static const char* FALSE_VALS[] = { "false", "False", "FALSE", 0 }; - - for (int i = 0; TRUE_VALS[i]; ++i) { - if (StringEqual(str, TRUE_VALS[i])) { - *value = true; - return true; - } - } - for (int i = 0; FALSE_VALS[i]; ++i) { - if (StringEqual(str, FALSE_VALS[i])) { - *value = false; - return true; - } - } - return false; -} - - -bool XMLUtil::ToFloat( const char* str, float* value ) -{ - if ( TIXML_SSCANF( str, "%f", value ) == 1 ) { - return true; - } - return false; -} - - -bool XMLUtil::ToDouble( const char* str, double* value ) -{ - if ( TIXML_SSCANF( str, "%lf", value ) == 1 ) { - return true; - } - return false; -} - - -bool XMLUtil::ToInt64(const char* str, int64_t* value) -{ - if (IsPrefixHex(str)) { - unsigned long long v = 0; // horrible syntax trick to make the compiler happy about %llx - if (TIXML_SSCANF(str, "%llx", &v) == 1) { - *value = static_cast(v); - return true; - } - } - else { - long long v = 0; // horrible syntax trick to make the compiler happy about %lld - if (TIXML_SSCANF(str, "%lld", &v) == 1) { - *value = static_cast(v); - return true; - } - } - return false; -} - - -bool XMLUtil::ToUnsigned64(const char* str, uint64_t* value) { - unsigned long long v = 0; // horrible syntax trick to make the compiler happy about %llu - if(TIXML_SSCANF(str, IsPrefixHex(str) ? "%llx" : "%llu", &v) == 1) { - *value = static_cast(v); - return true; - } - return false; -} - - -char* XMLDocument::Identify( char* p, XMLNode** node, bool first ) -{ - TIXMLASSERT( node ); - TIXMLASSERT( p ); - char* const start = p; - int const startLine = _parseCurLineNum; - p = XMLUtil::SkipWhiteSpace( p, &_parseCurLineNum ); - if( !*p ) { - *node = 0; - TIXMLASSERT( p ); - return p; - } - - // These strings define the matching patterns: - static const char* xmlHeader = { "( _commentPool ); - returnNode->_parseLineNum = _parseCurLineNum; - p += xmlHeaderLen; - } - else if ( XMLUtil::StringEqual( p, commentHeader, commentHeaderLen ) ) { - returnNode = CreateUnlinkedNode( _commentPool ); - returnNode->_parseLineNum = _parseCurLineNum; - p += commentHeaderLen; - } - else if ( XMLUtil::StringEqual( p, cdataHeader, cdataHeaderLen ) ) { - XMLText* text = CreateUnlinkedNode( _textPool ); - returnNode = text; - returnNode->_parseLineNum = _parseCurLineNum; - p += cdataHeaderLen; - text->SetCData( true ); - } - else if ( XMLUtil::StringEqual( p, dtdHeader, dtdHeaderLen ) ) { - returnNode = CreateUnlinkedNode( _commentPool ); - returnNode->_parseLineNum = _parseCurLineNum; - p += dtdHeaderLen; - } - else if ( XMLUtil::StringEqual( p, elementHeader, elementHeaderLen ) ) { - - // Preserve whitespace pedantically before closing tag, when it's immediately after opening tag - if (WhitespaceMode() == PEDANTIC_WHITESPACE && first && p != start && *(p + elementHeaderLen) == '/') { - returnNode = CreateUnlinkedNode(_textPool); - returnNode->_parseLineNum = startLine; - p = start; // Back it up, all the text counts. - _parseCurLineNum = startLine; - } - else { - returnNode = CreateUnlinkedNode(_elementPool); - returnNode->_parseLineNum = _parseCurLineNum; - p += elementHeaderLen; - } - } - else { - returnNode = CreateUnlinkedNode( _textPool ); - returnNode->_parseLineNum = _parseCurLineNum; // Report line of first non-whitespace character - p = start; // Back it up, all the text counts. - _parseCurLineNum = startLine; - } - - TIXMLASSERT( returnNode ); - TIXMLASSERT( p ); - *node = returnNode; - return p; -} - - -bool XMLDocument::Accept( XMLVisitor* visitor ) const -{ - TIXMLASSERT( visitor ); - if ( visitor->VisitEnter( *this ) ) { - for ( const XMLNode* node=FirstChild(); node; node=node->NextSibling() ) { - if ( !node->Accept( visitor ) ) { - break; - } - } - } - return visitor->VisitExit( *this ); -} - - -// --------- XMLNode ----------- // - -XMLNode::XMLNode( XMLDocument* doc ) : - _document( doc ), - _parent( 0 ), - _value(), - _parseLineNum( 0 ), - _firstChild( 0 ), _lastChild( 0 ), - _prev( 0 ), _next( 0 ), - _userData( 0 ), - _memPool( 0 ) -{ -} - - -XMLNode::~XMLNode() -{ - DeleteChildren(); - if ( _parent ) { - _parent->Unlink( this ); - } -} - -// ChildElementCount was originally suggested by msteiger on the sourceforge page for TinyXML and modified by KB1SPH for TinyXML-2. - -int XMLNode::ChildElementCount(const char *value) const { - int count = 0; - - const XMLElement *e = FirstChildElement(value); - - while (e) { - e = e->NextSiblingElement(value); - count++; - } - - return count; -} - -int XMLNode::ChildElementCount() const { - int count = 0; - - const XMLElement *e = FirstChildElement(); - - while (e) { - e = e->NextSiblingElement(); - count++; - } - - return count; -} - -const char* XMLNode::Value() const -{ - // Edge case: XMLDocuments don't have a Value. Return null. - if ( this->ToDocument() ) - return 0; - return _value.GetStr(); -} - -void XMLNode::SetValue( const char* str, bool staticMem ) -{ - if ( staticMem ) { - _value.SetInternedStr( str ); - } - else { - _value.SetStr( str ); - } -} - -XMLNode* XMLNode::DeepClone(XMLDocument* target) const -{ - XMLNode* clone = this->ShallowClone(target); - if (!clone) return 0; - - for (const XMLNode* child = this->FirstChild(); child; child = child->NextSibling()) { - XMLNode* childClone = child->DeepClone(target); - TIXMLASSERT(childClone); - clone->InsertEndChild(childClone); - } - return clone; -} - -void XMLNode::DeleteChildren() -{ - while( _firstChild ) { - TIXMLASSERT( _lastChild ); - DeleteChild( _firstChild ); - } - _firstChild = _lastChild = 0; -} - - -void XMLNode::Unlink( XMLNode* child ) -{ - TIXMLASSERT( child ); - TIXMLASSERT( child->_document == _document ); - TIXMLASSERT( child->_parent == this ); - if ( child == _firstChild ) { - _firstChild = _firstChild->_next; - } - if ( child == _lastChild ) { - _lastChild = _lastChild->_prev; - } - - if ( child->_prev ) { - child->_prev->_next = child->_next; - } - if ( child->_next ) { - child->_next->_prev = child->_prev; - } - child->_next = 0; - child->_prev = 0; - child->_parent = 0; -} - - -void XMLNode::DeleteChild( XMLNode* node ) -{ - TIXMLASSERT( node ); - TIXMLASSERT( node->_document == _document ); - TIXMLASSERT( node->_parent == this ); - Unlink( node ); - TIXMLASSERT(node->_prev == 0); - TIXMLASSERT(node->_next == 0); - TIXMLASSERT(node->_parent == 0); - DeleteNode( node ); -} - - -XMLNode* XMLNode::InsertEndChild( XMLNode* addThis ) -{ - TIXMLASSERT( addThis ); - if ( addThis->_document != _document ) { - TIXMLASSERT( false ); - return 0; - } - InsertChildPreamble( addThis ); - - if ( _lastChild ) { - TIXMLASSERT( _firstChild ); - TIXMLASSERT( _lastChild->_next == 0 ); - _lastChild->_next = addThis; - addThis->_prev = _lastChild; - _lastChild = addThis; - - addThis->_next = 0; - } - else { - TIXMLASSERT( _firstChild == 0 ); - _firstChild = _lastChild = addThis; - - addThis->_prev = 0; - addThis->_next = 0; - } - addThis->_parent = this; - return addThis; -} - - -XMLNode* XMLNode::InsertFirstChild( XMLNode* addThis ) -{ - TIXMLASSERT( addThis ); - if ( addThis->_document != _document ) { - TIXMLASSERT( false ); - return 0; - } - InsertChildPreamble( addThis ); - - if ( _firstChild ) { - TIXMLASSERT( _lastChild ); - TIXMLASSERT( _firstChild->_prev == 0 ); - - _firstChild->_prev = addThis; - addThis->_next = _firstChild; - _firstChild = addThis; - - addThis->_prev = 0; - } - else { - TIXMLASSERT( _lastChild == 0 ); - _firstChild = _lastChild = addThis; - - addThis->_prev = 0; - addThis->_next = 0; - } - addThis->_parent = this; - return addThis; -} - - -XMLNode* XMLNode::InsertAfterChild( XMLNode* afterThis, XMLNode* addThis ) -{ - TIXMLASSERT( addThis ); - if ( addThis->_document != _document ) { - TIXMLASSERT( false ); - return 0; - } - - TIXMLASSERT( afterThis ); - - if ( afterThis->_parent != this ) { - TIXMLASSERT( false ); - return 0; - } - if ( afterThis == addThis ) { - // Current state: BeforeThis -> AddThis -> OneAfterAddThis - // Now AddThis must disappear from it's location and then - // reappear between BeforeThis and OneAfterAddThis. - // So just leave it where it is. - return addThis; - } - - if ( afterThis->_next == 0 ) { - // The last node or the only node. - return InsertEndChild( addThis ); - } - InsertChildPreamble( addThis ); - addThis->_prev = afterThis; - addThis->_next = afterThis->_next; - afterThis->_next->_prev = addThis; - afterThis->_next = addThis; - addThis->_parent = this; - return addThis; -} - - - - -const XMLElement* XMLNode::FirstChildElement( const char* name ) const -{ - for( const XMLNode* node = _firstChild; node; node = node->_next ) { - const XMLElement* element = node->ToElementWithName( name ); - if ( element ) { - return element; - } - } - return 0; -} - - -const XMLElement* XMLNode::LastChildElement( const char* name ) const -{ - for( const XMLNode* node = _lastChild; node; node = node->_prev ) { - const XMLElement* element = node->ToElementWithName( name ); - if ( element ) { - return element; - } - } - return 0; -} - - -const XMLElement* XMLNode::NextSiblingElement( const char* name ) const -{ - for( const XMLNode* node = _next; node; node = node->_next ) { - const XMLElement* element = node->ToElementWithName( name ); - if ( element ) { - return element; - } - } - return 0; -} - - -const XMLElement* XMLNode::PreviousSiblingElement( const char* name ) const -{ - for( const XMLNode* node = _prev; node; node = node->_prev ) { - const XMLElement* element = node->ToElementWithName( name ); - if ( element ) { - return element; - } - } - return 0; -} - - -char* XMLNode::ParseDeep( char* p, StrPair* parentEndTag, int* curLineNumPtr ) -{ - // This is a recursive method, but thinking about it "at the current level" - // it is a pretty simple flat list: - // - // - // - // With a special case: - // - // - // - // - // Where the closing element (/foo) *must* be the next thing after the opening - // element, and the names must match. BUT the tricky bit is that the closing - // element will be read by the child. - // - // 'endTag' is the end tag for this node, it is returned by a call to a child. - // 'parentEnd' is the end tag for the parent, which is filled in and returned. - - XMLDocument::DepthTracker tracker(_document); - if (_document->Error()) - return 0; - - bool first = true; - while( p && *p ) { - XMLNode* node = 0; - - p = _document->Identify( p, &node, first ); - TIXMLASSERT( p ); - if ( node == 0 ) { - break; - } - first = false; - - const int initialLineNum = node->_parseLineNum; - - StrPair endTag; - p = node->ParseDeep( p, &endTag, curLineNumPtr ); - if ( !p ) { - _document->DeleteNode( node ); - if ( !_document->Error() ) { - _document->SetError( XML_ERROR_PARSING, initialLineNum, 0); - } - break; - } - - const XMLDeclaration* const decl = node->ToDeclaration(); - if ( decl ) { - // Declarations are only allowed at document level - // - // Multiple declarations are allowed but all declarations - // must occur before anything else. - // - // Optimized due to a security test case. If the first node is - // a declaration, and the last node is a declaration, then only - // declarations have so far been added. - bool wellLocated = false; - - if (ToDocument()) { - if (FirstChild()) { - wellLocated = - FirstChild() && - FirstChild()->ToDeclaration() && - LastChild() && - LastChild()->ToDeclaration(); - } - else { - wellLocated = true; - } - } - if ( !wellLocated ) { - _document->SetError( XML_ERROR_PARSING_DECLARATION, initialLineNum, "XMLDeclaration value=%s", decl->Value()); - _document->DeleteNode( node ); - break; - } - } - - XMLElement* ele = node->ToElement(); - if ( ele ) { - // We read the end tag. Return it to the parent. - if ( ele->ClosingType() == XMLElement::CLOSING ) { - if ( parentEndTag ) { - ele->_value.TransferTo( parentEndTag ); - } - node->_memPool->SetTracked(); // created and then immediately deleted. - DeleteNode( node ); - return p; - } - - // Handle an end tag returned to this level. - // And handle a bunch of annoying errors. - bool mismatch = false; - if ( endTag.Empty() ) { - if ( ele->ClosingType() == XMLElement::OPEN ) { - mismatch = true; - } - } - else { - if ( ele->ClosingType() != XMLElement::OPEN ) { - mismatch = true; - } - else if ( !XMLUtil::StringEqual( endTag.GetStr(), ele->Name() ) ) { - mismatch = true; - } - } - if ( mismatch ) { - _document->SetError( XML_ERROR_MISMATCHED_ELEMENT, initialLineNum, "XMLElement name=%s", ele->Name()); - _document->DeleteNode( node ); - break; - } - } - InsertEndChild( node ); - } - return 0; -} - -/*static*/ void XMLNode::DeleteNode( XMLNode* node ) -{ - if ( node == 0 ) { - return; - } - TIXMLASSERT(node->_document); - if (!node->ToDocument()) { - node->_document->MarkInUse(node); - } - - MemPool* pool = node->_memPool; - node->~XMLNode(); - pool->Free( node ); -} - -void XMLNode::InsertChildPreamble( XMLNode* insertThis ) const -{ - TIXMLASSERT( insertThis ); - TIXMLASSERT( insertThis->_document == _document ); - - if (insertThis->_parent) { - insertThis->_parent->Unlink( insertThis ); - } - else { - insertThis->_document->MarkInUse(insertThis); - insertThis->_memPool->SetTracked(); - } -} - -const XMLElement* XMLNode::ToElementWithName( const char* name ) const -{ - const XMLElement* element = this->ToElement(); - if ( element == 0 ) { - return 0; - } - if ( name == 0 ) { - return element; - } - if ( XMLUtil::StringEqual( element->Name(), name ) ) { - return element; - } - return 0; -} - -// --------- XMLText ---------- // -char* XMLText::ParseDeep( char* p, StrPair*, int* curLineNumPtr ) -{ - if ( this->CData() ) { - p = _value.ParseText( p, "]]>", StrPair::NEEDS_NEWLINE_NORMALIZATION, curLineNumPtr ); - if ( !p ) { - _document->SetError( XML_ERROR_PARSING_CDATA, _parseLineNum, 0 ); - } - return p; - } - else { - int flags = _document->ProcessEntities() ? StrPair::TEXT_ELEMENT : StrPair::TEXT_ELEMENT_LEAVE_ENTITIES; - if ( _document->WhitespaceMode() == COLLAPSE_WHITESPACE ) { - flags |= StrPair::NEEDS_WHITESPACE_COLLAPSING; - } - - p = _value.ParseText( p, "<", flags, curLineNumPtr ); - if ( p && *p ) { - return p-1; - } - if ( !p ) { - _document->SetError( XML_ERROR_PARSING_TEXT, _parseLineNum, 0 ); - } - } - return 0; -} - - -XMLNode* XMLText::ShallowClone( XMLDocument* doc ) const -{ - if ( !doc ) { - doc = _document; - } - XMLText* text = doc->NewText( Value() ); // fixme: this will always allocate memory. Intern? - text->SetCData( this->CData() ); - return text; -} - - -bool XMLText::ShallowEqual( const XMLNode* compare ) const -{ - TIXMLASSERT( compare ); - const XMLText* text = compare->ToText(); - return ( text && XMLUtil::StringEqual( text->Value(), Value() ) ); -} - - -bool XMLText::Accept( XMLVisitor* visitor ) const -{ - TIXMLASSERT( visitor ); - return visitor->Visit( *this ); -} - - -// --------- XMLComment ---------- // - -XMLComment::XMLComment( XMLDocument* doc ) : XMLNode( doc ) -{ -} - - -XMLComment::~XMLComment() -{ -} - - -char* XMLComment::ParseDeep( char* p, StrPair*, int* curLineNumPtr ) -{ - // Comment parses as text. - p = _value.ParseText( p, "-->", StrPair::COMMENT, curLineNumPtr ); - if ( p == 0 ) { - _document->SetError( XML_ERROR_PARSING_COMMENT, _parseLineNum, 0 ); - } - return p; -} - - -XMLNode* XMLComment::ShallowClone( XMLDocument* doc ) const -{ - if ( !doc ) { - doc = _document; - } - XMLComment* comment = doc->NewComment( Value() ); // fixme: this will always allocate memory. Intern? - return comment; -} - - -bool XMLComment::ShallowEqual( const XMLNode* compare ) const -{ - TIXMLASSERT( compare ); - const XMLComment* comment = compare->ToComment(); - return ( comment && XMLUtil::StringEqual( comment->Value(), Value() )); -} - - -bool XMLComment::Accept( XMLVisitor* visitor ) const -{ - TIXMLASSERT( visitor ); - return visitor->Visit( *this ); -} - - -// --------- XMLDeclaration ---------- // - -XMLDeclaration::XMLDeclaration( XMLDocument* doc ) : XMLNode( doc ) -{ -} - - -XMLDeclaration::~XMLDeclaration() -{ - //printf( "~XMLDeclaration\n" ); -} - - -char* XMLDeclaration::ParseDeep( char* p, StrPair*, int* curLineNumPtr ) -{ - // Declaration parses as text. - p = _value.ParseText( p, "?>", StrPair::NEEDS_NEWLINE_NORMALIZATION, curLineNumPtr ); - if ( p == 0 ) { - _document->SetError( XML_ERROR_PARSING_DECLARATION, _parseLineNum, 0 ); - } - return p; -} - - -XMLNode* XMLDeclaration::ShallowClone( XMLDocument* doc ) const -{ - if ( !doc ) { - doc = _document; - } - XMLDeclaration* dec = doc->NewDeclaration( Value() ); // fixme: this will always allocate memory. Intern? - return dec; -} - - -bool XMLDeclaration::ShallowEqual( const XMLNode* compare ) const -{ - TIXMLASSERT( compare ); - const XMLDeclaration* declaration = compare->ToDeclaration(); - return ( declaration && XMLUtil::StringEqual( declaration->Value(), Value() )); -} - - - -bool XMLDeclaration::Accept( XMLVisitor* visitor ) const -{ - TIXMLASSERT( visitor ); - return visitor->Visit( *this ); -} - -// --------- XMLUnknown ---------- // - -XMLUnknown::XMLUnknown( XMLDocument* doc ) : XMLNode( doc ) -{ -} - - -XMLUnknown::~XMLUnknown() -{ -} - - -char* XMLUnknown::ParseDeep( char* p, StrPair*, int* curLineNumPtr ) -{ - // Unknown parses as text. - p = _value.ParseText( p, ">", StrPair::NEEDS_NEWLINE_NORMALIZATION, curLineNumPtr ); - if ( !p ) { - _document->SetError( XML_ERROR_PARSING_UNKNOWN, _parseLineNum, 0 ); - } - return p; -} - - -XMLNode* XMLUnknown::ShallowClone( XMLDocument* doc ) const -{ - if ( !doc ) { - doc = _document; - } - XMLUnknown* text = doc->NewUnknown( Value() ); // fixme: this will always allocate memory. Intern? - return text; -} - - -bool XMLUnknown::ShallowEqual( const XMLNode* compare ) const -{ - TIXMLASSERT( compare ); - const XMLUnknown* unknown = compare->ToUnknown(); - return ( unknown && XMLUtil::StringEqual( unknown->Value(), Value() )); -} - - -bool XMLUnknown::Accept( XMLVisitor* visitor ) const -{ - TIXMLASSERT( visitor ); - return visitor->Visit( *this ); -} - -// --------- XMLAttribute ---------- // - -const char* XMLAttribute::Name() const -{ - return _name.GetStr(); -} - -const char* XMLAttribute::Value() const -{ - return _value.GetStr(); -} - -char* XMLAttribute::ParseDeep( char* p, bool processEntities, int* curLineNumPtr ) -{ - // Parse using the name rules: bug fix, was using ParseText before - p = _name.ParseName( p ); - if ( !p || !*p ) { - return 0; - } - - // Skip white space before = - p = XMLUtil::SkipWhiteSpace( p, curLineNumPtr ); - if ( *p != '=' ) { - return 0; - } - - ++p; // move up to opening quote - p = XMLUtil::SkipWhiteSpace( p, curLineNumPtr ); - if ( *p != '\"' && *p != '\'' ) { - return 0; - } - - const char endTag[2] = { *p, 0 }; - ++p; // move past opening quote - - p = _value.ParseText( p, endTag, processEntities ? StrPair::ATTRIBUTE_VALUE : StrPair::ATTRIBUTE_VALUE_LEAVE_ENTITIES, curLineNumPtr ); - return p; -} - - -void XMLAttribute::SetName( const char* n ) -{ - _name.SetStr( n ); -} - - -XMLError XMLAttribute::QueryIntValue( int* value ) const -{ - if ( XMLUtil::ToInt( Value(), value )) { - return XML_SUCCESS; - } - return XML_WRONG_ATTRIBUTE_TYPE; -} - - -XMLError XMLAttribute::QueryUnsignedValue( unsigned int* value ) const -{ - if ( XMLUtil::ToUnsigned( Value(), value )) { - return XML_SUCCESS; - } - return XML_WRONG_ATTRIBUTE_TYPE; -} - - -XMLError XMLAttribute::QueryInt64Value(int64_t* value) const -{ - if (XMLUtil::ToInt64(Value(), value)) { - return XML_SUCCESS; - } - return XML_WRONG_ATTRIBUTE_TYPE; -} - - -XMLError XMLAttribute::QueryUnsigned64Value(uint64_t* value) const -{ - if(XMLUtil::ToUnsigned64(Value(), value)) { - return XML_SUCCESS; - } - return XML_WRONG_ATTRIBUTE_TYPE; -} - - -XMLError XMLAttribute::QueryBoolValue( bool* value ) const -{ - if ( XMLUtil::ToBool( Value(), value )) { - return XML_SUCCESS; - } - return XML_WRONG_ATTRIBUTE_TYPE; -} - - -XMLError XMLAttribute::QueryFloatValue( float* value ) const -{ - if ( XMLUtil::ToFloat( Value(), value )) { - return XML_SUCCESS; - } - return XML_WRONG_ATTRIBUTE_TYPE; -} - - -XMLError XMLAttribute::QueryDoubleValue( double* value ) const -{ - if ( XMLUtil::ToDouble( Value(), value )) { - return XML_SUCCESS; - } - return XML_WRONG_ATTRIBUTE_TYPE; -} - - -void XMLAttribute::SetAttribute( const char* v ) -{ - _value.SetStr( v ); -} - - -void XMLAttribute::SetAttribute( int v ) -{ - char buf[BUF_SIZE]; - XMLUtil::ToStr( v, buf, BUF_SIZE ); - _value.SetStr( buf ); -} - - -void XMLAttribute::SetAttribute( unsigned v ) -{ - char buf[BUF_SIZE]; - XMLUtil::ToStr( v, buf, BUF_SIZE ); - _value.SetStr( buf ); -} - - -void XMLAttribute::SetAttribute(int64_t v) -{ - char buf[BUF_SIZE]; - XMLUtil::ToStr(v, buf, BUF_SIZE); - _value.SetStr(buf); -} - -void XMLAttribute::SetAttribute(uint64_t v) -{ - char buf[BUF_SIZE]; - XMLUtil::ToStr(v, buf, BUF_SIZE); - _value.SetStr(buf); -} - - -void XMLAttribute::SetAttribute( bool v ) -{ - char buf[BUF_SIZE]; - XMLUtil::ToStr( v, buf, BUF_SIZE ); - _value.SetStr( buf ); -} - -void XMLAttribute::SetAttribute( double v ) -{ - char buf[BUF_SIZE]; - XMLUtil::ToStr( v, buf, BUF_SIZE ); - _value.SetStr( buf ); -} - -void XMLAttribute::SetAttribute( float v ) -{ - char buf[BUF_SIZE]; - XMLUtil::ToStr( v, buf, BUF_SIZE ); - _value.SetStr( buf ); -} - - -// --------- XMLElement ---------- // -XMLElement::XMLElement( XMLDocument* doc ) : XMLNode( doc ), - _closingType( OPEN ), - _rootAttribute( 0 ) -{ -} - - -XMLElement::~XMLElement() -{ - while( _rootAttribute ) { - XMLAttribute* next = _rootAttribute->_next; - DeleteAttribute( _rootAttribute ); - _rootAttribute = next; - } -} - - -const XMLAttribute* XMLElement::FindAttribute( const char* name ) const -{ - for( XMLAttribute* a = _rootAttribute; a; a = a->_next ) { - if ( XMLUtil::StringEqual( a->Name(), name ) ) { - return a; - } - } - return 0; -} - - -const char* XMLElement::Attribute( const char* name, const char* value ) const -{ - const XMLAttribute* a = FindAttribute( name ); - if ( !a ) { - return 0; - } - if ( !value || XMLUtil::StringEqual( a->Value(), value )) { - return a->Value(); - } - return 0; -} - -int XMLElement::IntAttribute(const char* name, int defaultValue) const -{ - int i = defaultValue; - QueryIntAttribute(name, &i); - return i; -} - -unsigned XMLElement::UnsignedAttribute(const char* name, unsigned defaultValue) const -{ - unsigned i = defaultValue; - QueryUnsignedAttribute(name, &i); - return i; -} - -int64_t XMLElement::Int64Attribute(const char* name, int64_t defaultValue) const -{ - int64_t i = defaultValue; - QueryInt64Attribute(name, &i); - return i; -} - -uint64_t XMLElement::Unsigned64Attribute(const char* name, uint64_t defaultValue) const -{ - uint64_t i = defaultValue; - QueryUnsigned64Attribute(name, &i); - return i; -} - -bool XMLElement::BoolAttribute(const char* name, bool defaultValue) const -{ - bool b = defaultValue; - QueryBoolAttribute(name, &b); - return b; -} - -double XMLElement::DoubleAttribute(const char* name, double defaultValue) const -{ - double d = defaultValue; - QueryDoubleAttribute(name, &d); - return d; -} - -float XMLElement::FloatAttribute(const char* name, float defaultValue) const -{ - float f = defaultValue; - QueryFloatAttribute(name, &f); - return f; -} - -const char* XMLElement::GetText() const -{ - /* skip comment node */ - const XMLNode* node = FirstChild(); - while (node) { - if (node->ToComment()) { - node = node->NextSibling(); - continue; - } - break; - } - - if ( node && node->ToText() ) { - return node->Value(); - } - return 0; -} - - -void XMLElement::SetText( const char* inText ) -{ - if ( FirstChild() && FirstChild()->ToText() ) - FirstChild()->SetValue( inText ); - else { - XMLText* theText = GetDocument()->NewText( inText ); - InsertFirstChild( theText ); - } -} - - -void XMLElement::SetText( int v ) -{ - char buf[BUF_SIZE]; - XMLUtil::ToStr( v, buf, BUF_SIZE ); - SetText( buf ); -} - - -void XMLElement::SetText( unsigned v ) -{ - char buf[BUF_SIZE]; - XMLUtil::ToStr( v, buf, BUF_SIZE ); - SetText( buf ); -} - - -void XMLElement::SetText(int64_t v) -{ - char buf[BUF_SIZE]; - XMLUtil::ToStr(v, buf, BUF_SIZE); - SetText(buf); -} - -void XMLElement::SetText(uint64_t v) { - char buf[BUF_SIZE]; - XMLUtil::ToStr(v, buf, BUF_SIZE); - SetText(buf); -} - - -void XMLElement::SetText( bool v ) -{ - char buf[BUF_SIZE]; - XMLUtil::ToStr( v, buf, BUF_SIZE ); - SetText( buf ); -} - - -void XMLElement::SetText( float v ) -{ - char buf[BUF_SIZE]; - XMLUtil::ToStr( v, buf, BUF_SIZE ); - SetText( buf ); -} - - -void XMLElement::SetText( double v ) -{ - char buf[BUF_SIZE]; - XMLUtil::ToStr( v, buf, BUF_SIZE ); - SetText( buf ); -} - - -XMLError XMLElement::QueryIntText( int* ival ) const -{ - if ( FirstChild() && FirstChild()->ToText() ) { - const char* t = FirstChild()->Value(); - if ( XMLUtil::ToInt( t, ival ) ) { - return XML_SUCCESS; - } - return XML_CAN_NOT_CONVERT_TEXT; - } - return XML_NO_TEXT_NODE; -} - - -XMLError XMLElement::QueryUnsignedText( unsigned* uval ) const -{ - if ( FirstChild() && FirstChild()->ToText() ) { - const char* t = FirstChild()->Value(); - if ( XMLUtil::ToUnsigned( t, uval ) ) { - return XML_SUCCESS; - } - return XML_CAN_NOT_CONVERT_TEXT; - } - return XML_NO_TEXT_NODE; -} - - -XMLError XMLElement::QueryInt64Text(int64_t* ival) const -{ - if (FirstChild() && FirstChild()->ToText()) { - const char* t = FirstChild()->Value(); - if (XMLUtil::ToInt64(t, ival)) { - return XML_SUCCESS; - } - return XML_CAN_NOT_CONVERT_TEXT; - } - return XML_NO_TEXT_NODE; -} - - -XMLError XMLElement::QueryUnsigned64Text(uint64_t* uval) const -{ - if(FirstChild() && FirstChild()->ToText()) { - const char* t = FirstChild()->Value(); - if(XMLUtil::ToUnsigned64(t, uval)) { - return XML_SUCCESS; - } - return XML_CAN_NOT_CONVERT_TEXT; - } - return XML_NO_TEXT_NODE; -} - - -XMLError XMLElement::QueryBoolText( bool* bval ) const -{ - if ( FirstChild() && FirstChild()->ToText() ) { - const char* t = FirstChild()->Value(); - if ( XMLUtil::ToBool( t, bval ) ) { - return XML_SUCCESS; - } - return XML_CAN_NOT_CONVERT_TEXT; - } - return XML_NO_TEXT_NODE; -} - - -XMLError XMLElement::QueryDoubleText( double* dval ) const -{ - if ( FirstChild() && FirstChild()->ToText() ) { - const char* t = FirstChild()->Value(); - if ( XMLUtil::ToDouble( t, dval ) ) { - return XML_SUCCESS; - } - return XML_CAN_NOT_CONVERT_TEXT; - } - return XML_NO_TEXT_NODE; -} - - -XMLError XMLElement::QueryFloatText( float* fval ) const -{ - if ( FirstChild() && FirstChild()->ToText() ) { - const char* t = FirstChild()->Value(); - if ( XMLUtil::ToFloat( t, fval ) ) { - return XML_SUCCESS; - } - return XML_CAN_NOT_CONVERT_TEXT; - } - return XML_NO_TEXT_NODE; -} - -int XMLElement::IntText(int defaultValue) const -{ - int i = defaultValue; - QueryIntText(&i); - return i; -} - -unsigned XMLElement::UnsignedText(unsigned defaultValue) const -{ - unsigned i = defaultValue; - QueryUnsignedText(&i); - return i; -} - -int64_t XMLElement::Int64Text(int64_t defaultValue) const -{ - int64_t i = defaultValue; - QueryInt64Text(&i); - return i; -} - -uint64_t XMLElement::Unsigned64Text(uint64_t defaultValue) const -{ - uint64_t i = defaultValue; - QueryUnsigned64Text(&i); - return i; -} - -bool XMLElement::BoolText(bool defaultValue) const -{ - bool b = defaultValue; - QueryBoolText(&b); - return b; -} - -double XMLElement::DoubleText(double defaultValue) const -{ - double d = defaultValue; - QueryDoubleText(&d); - return d; -} - -float XMLElement::FloatText(float defaultValue) const -{ - float f = defaultValue; - QueryFloatText(&f); - return f; -} - - -XMLAttribute* XMLElement::FindOrCreateAttribute( const char* name ) -{ - XMLAttribute* last = 0; - XMLAttribute* attrib = 0; - for( attrib = _rootAttribute; - attrib; - last = attrib, attrib = attrib->_next ) { - if ( XMLUtil::StringEqual( attrib->Name(), name ) ) { - break; - } - } - if ( !attrib ) { - attrib = CreateAttribute(); - TIXMLASSERT( attrib ); - if ( last ) { - TIXMLASSERT( last->_next == 0 ); - last->_next = attrib; - } - else { - TIXMLASSERT( _rootAttribute == 0 ); - _rootAttribute = attrib; - } - attrib->SetName( name ); - } - return attrib; -} - - -void XMLElement::DeleteAttribute( const char* name ) -{ - XMLAttribute* prev = 0; - for( XMLAttribute* a=_rootAttribute; a; a=a->_next ) { - if ( XMLUtil::StringEqual( name, a->Name() ) ) { - if ( prev ) { - prev->_next = a->_next; - } - else { - _rootAttribute = a->_next; - } - DeleteAttribute( a ); - break; - } - prev = a; - } -} - - -char* XMLElement::ParseAttributes( char* p, int* curLineNumPtr ) -{ - XMLAttribute* prevAttribute = 0; - - // Read the attributes. - while( p ) { - p = XMLUtil::SkipWhiteSpace( p, curLineNumPtr ); - if ( !(*p) ) { - _document->SetError( XML_ERROR_PARSING_ELEMENT, _parseLineNum, "XMLElement name=%s", Name() ); - return 0; - } - - // attribute. - if (XMLUtil::IsNameStartChar( static_cast(*p) ) ) { - XMLAttribute* attrib = CreateAttribute(); - TIXMLASSERT( attrib ); - attrib->_parseLineNum = _document->_parseCurLineNum; - - const int attrLineNum = attrib->_parseLineNum; - - p = attrib->ParseDeep( p, _document->ProcessEntities(), curLineNumPtr ); - if ( !p || Attribute( attrib->Name() ) ) { - DeleteAttribute( attrib ); - _document->SetError( XML_ERROR_PARSING_ATTRIBUTE, attrLineNum, "XMLElement name=%s", Name() ); - return 0; - } - // There is a minor bug here: if the attribute in the source xml - // document is duplicated, it will not be detected and the - // attribute will be doubly added. However, tracking the 'prevAttribute' - // avoids re-scanning the attribute list. Preferring performance for - // now, may reconsider in the future. - if ( prevAttribute ) { - TIXMLASSERT( prevAttribute->_next == 0 ); - prevAttribute->_next = attrib; - } - else { - TIXMLASSERT( _rootAttribute == 0 ); - _rootAttribute = attrib; - } - prevAttribute = attrib; - } - // end of the tag - else if ( *p == '>' ) { - ++p; - break; - } - // end of the tag - else if ( *p == '/' && *(p+1) == '>' ) { - _closingType = CLOSED; - return p+2; // done; sealed element. - } - else { - _document->SetError( XML_ERROR_PARSING_ELEMENT, _parseLineNum, 0 ); - return 0; - } - } - return p; -} - -void XMLElement::DeleteAttribute( XMLAttribute* attribute ) -{ - if ( attribute == 0 ) { - return; - } - MemPool* pool = attribute->_memPool; - attribute->~XMLAttribute(); - pool->Free( attribute ); -} - -XMLAttribute* XMLElement::CreateAttribute() -{ - TIXMLASSERT( sizeof( XMLAttribute ) == _document->_attributePool.ItemSize() ); - XMLAttribute* attrib = new (_document->_attributePool.Alloc() ) XMLAttribute(); - TIXMLASSERT( attrib ); - attrib->_memPool = &_document->_attributePool; - attrib->_memPool->SetTracked(); - return attrib; -} - - -XMLElement* XMLElement::InsertNewChildElement(const char* name) -{ - XMLElement* node = _document->NewElement(name); - return InsertEndChild(node) ? node : 0; -} - -XMLComment* XMLElement::InsertNewComment(const char* comment) -{ - XMLComment* node = _document->NewComment(comment); - return InsertEndChild(node) ? node : 0; -} - -XMLText* XMLElement::InsertNewText(const char* text) -{ - XMLText* node = _document->NewText(text); - return InsertEndChild(node) ? node : 0; -} - -XMLDeclaration* XMLElement::InsertNewDeclaration(const char* text) -{ - XMLDeclaration* node = _document->NewDeclaration(text); - return InsertEndChild(node) ? node : 0; -} - -XMLUnknown* XMLElement::InsertNewUnknown(const char* text) -{ - XMLUnknown* node = _document->NewUnknown(text); - return InsertEndChild(node) ? node : 0; -} - - - -// -// -// foobar -// -char* XMLElement::ParseDeep( char* p, StrPair* parentEndTag, int* curLineNumPtr ) -{ - // Read the element name. - p = XMLUtil::SkipWhiteSpace( p, curLineNumPtr ); - - // The closing element is the form. It is - // parsed just like a regular element then deleted from - // the DOM. - if ( *p == '/' ) { - _closingType = CLOSING; - ++p; - } - - p = _value.ParseName( p ); - if ( _value.Empty() ) { - return 0; - } - - p = ParseAttributes( p, curLineNumPtr ); - if ( !p || !*p || _closingType != OPEN ) { - return p; - } - - p = XMLNode::ParseDeep( p, parentEndTag, curLineNumPtr ); - return p; -} - - - -XMLNode* XMLElement::ShallowClone( XMLDocument* doc ) const -{ - if ( !doc ) { - doc = _document; - } - XMLElement* element = doc->NewElement( Value() ); // fixme: this will always allocate memory. Intern? - for( const XMLAttribute* a=FirstAttribute(); a; a=a->Next() ) { - element->SetAttribute( a->Name(), a->Value() ); // fixme: this will always allocate memory. Intern? - } - return element; -} - - -bool XMLElement::ShallowEqual( const XMLNode* compare ) const -{ - TIXMLASSERT( compare ); - const XMLElement* other = compare->ToElement(); - if ( other && XMLUtil::StringEqual( other->Name(), Name() )) { - - const XMLAttribute* a=FirstAttribute(); - const XMLAttribute* b=other->FirstAttribute(); - - while ( a && b ) { - if ( !XMLUtil::StringEqual( a->Value(), b->Value() ) ) { - return false; - } - a = a->Next(); - b = b->Next(); - } - if ( a || b ) { - // different count - return false; - } - return true; - } - return false; -} - - -bool XMLElement::Accept( XMLVisitor* visitor ) const -{ - TIXMLASSERT( visitor ); - if ( visitor->VisitEnter( *this, _rootAttribute ) ) { - for ( const XMLNode* node=FirstChild(); node; node=node->NextSibling() ) { - if ( !node->Accept( visitor ) ) { - break; - } - } - } - return visitor->VisitExit( *this ); -} - - -// --------- XMLDocument ----------- // - -// Warning: List must match 'enum XMLError' -const char* XMLDocument::_errorNames[XML_ERROR_COUNT] = { - "XML_SUCCESS", - "XML_NO_ATTRIBUTE", - "XML_WRONG_ATTRIBUTE_TYPE", - "XML_ERROR_FILE_NOT_FOUND", - "XML_ERROR_FILE_COULD_NOT_BE_OPENED", - "XML_ERROR_FILE_READ_ERROR", - "XML_ERROR_PARSING_ELEMENT", - "XML_ERROR_PARSING_ATTRIBUTE", - "XML_ERROR_PARSING_TEXT", - "XML_ERROR_PARSING_CDATA", - "XML_ERROR_PARSING_COMMENT", - "XML_ERROR_PARSING_DECLARATION", - "XML_ERROR_PARSING_UNKNOWN", - "XML_ERROR_EMPTY_DOCUMENT", - "XML_ERROR_MISMATCHED_ELEMENT", - "XML_ERROR_PARSING", - "XML_CAN_NOT_CONVERT_TEXT", - "XML_NO_TEXT_NODE", - "XML_ELEMENT_DEPTH_EXCEEDED" -}; - - -XMLDocument::XMLDocument( bool processEntities, Whitespace whitespaceMode ) : - XMLNode( 0 ), - _writeBOM( false ), - _processEntities( processEntities ), - _errorID(XML_SUCCESS), - _whitespaceMode( whitespaceMode ), - _errorStr(), - _errorLineNum( 0 ), - _charBuffer( 0 ), - _parseCurLineNum( 0 ), - _parsingDepth(0), - _unlinked(), - _elementPool(), - _attributePool(), - _textPool(), - _commentPool() -{ - // avoid VC++ C4355 warning about 'this' in initializer list (C4355 is off by default in VS2012+) - _document = this; -} - - -XMLDocument::~XMLDocument() -{ - Clear(); -} - - -void XMLDocument::MarkInUse(const XMLNode* const node) -{ - TIXMLASSERT(node); - TIXMLASSERT(node->_parent == 0); - - for (size_t i = 0; i < _unlinked.Size(); ++i) { - if (node == _unlinked[i]) { - _unlinked.SwapRemove(i); - break; - } - } -} - -void XMLDocument::Clear() -{ - DeleteChildren(); - while( _unlinked.Size()) { - DeleteNode(_unlinked[0]); // Will remove from _unlinked as part of delete. - } - -#ifdef TINYXML2_DEBUG - const bool hadError = Error(); -#endif - ClearError(); - - delete [] _charBuffer; - _charBuffer = 0; - _parsingDepth = 0; - -#if 0 - _textPool.Trace( "text" ); - _elementPool.Trace( "element" ); - _commentPool.Trace( "comment" ); - _attributePool.Trace( "attribute" ); -#endif - -#ifdef TINYXML2_DEBUG - if ( !hadError ) { - TIXMLASSERT( _elementPool.CurrentAllocs() == _elementPool.Untracked() ); - TIXMLASSERT( _attributePool.CurrentAllocs() == _attributePool.Untracked() ); - TIXMLASSERT( _textPool.CurrentAllocs() == _textPool.Untracked() ); - TIXMLASSERT( _commentPool.CurrentAllocs() == _commentPool.Untracked() ); - } -#endif -} - - -void XMLDocument::DeepCopy(XMLDocument* target) const -{ - TIXMLASSERT(target); - if (target == this) { - return; // technically success - a no-op. - } - - target->Clear(); - for (const XMLNode* node = this->FirstChild(); node; node = node->NextSibling()) { - target->InsertEndChild(node->DeepClone(target)); - } -} - -XMLElement* XMLDocument::NewElement( const char* name ) -{ - XMLElement* ele = CreateUnlinkedNode( _elementPool ); - ele->SetName( name ); - return ele; -} - - -XMLComment* XMLDocument::NewComment( const char* str ) -{ - XMLComment* comment = CreateUnlinkedNode( _commentPool ); - comment->SetValue( str ); - return comment; -} - - -XMLText* XMLDocument::NewText( const char* str ) -{ - XMLText* text = CreateUnlinkedNode( _textPool ); - text->SetValue( str ); - return text; -} - - -XMLDeclaration* XMLDocument::NewDeclaration( const char* str ) -{ - XMLDeclaration* dec = CreateUnlinkedNode( _commentPool ); - dec->SetValue( str ? str : "xml version=\"1.0\" encoding=\"UTF-8\"" ); - return dec; -} - - -XMLUnknown* XMLDocument::NewUnknown( const char* str ) -{ - XMLUnknown* unk = CreateUnlinkedNode( _commentPool ); - unk->SetValue( str ); - return unk; -} - -static FILE* callfopen( const char* filepath, const char* mode ) -{ - TIXMLASSERT( filepath ); - TIXMLASSERT( mode ); -#if defined(_MSC_VER) && (_MSC_VER >= 1400 ) && (!defined WINCE) - FILE* fp = 0; - const errno_t err = fopen_s( &fp, filepath, mode ); - if ( err ) { - return 0; - } -#else - FILE* fp = fopen( filepath, mode ); -#endif - return fp; -} - -void XMLDocument::DeleteNode( XMLNode* node ) { - TIXMLASSERT( node ); - TIXMLASSERT(node->_document == this ); - if (node->_parent) { - node->_parent->DeleteChild( node ); - } - else { - // Isn't in the tree. - // Use the parent delete. - // Also, we need to mark it tracked: we 'know' - // it was never used. - node->_memPool->SetTracked(); - // Call the static XMLNode version: - XMLNode::DeleteNode(node); - } -} - - -XMLError XMLDocument::LoadFile( const char* filename ) -{ - if ( !filename ) { - TIXMLASSERT( false ); - SetError( XML_ERROR_FILE_COULD_NOT_BE_OPENED, 0, "filename=" ); - return _errorID; - } - - Clear(); - FILE* fp = callfopen( filename, "rb" ); - if ( !fp ) { - SetError( XML_ERROR_FILE_NOT_FOUND, 0, "filename=%s", filename ); - return _errorID; - } - LoadFile( fp ); - fclose( fp ); - return _errorID; -} - -XMLError XMLDocument::LoadFile( FILE* fp ) -{ - Clear(); - - TIXML_FSEEK( fp, 0, SEEK_SET ); - if ( fgetc( fp ) == EOF && ferror( fp ) != 0 ) { - SetError( XML_ERROR_FILE_READ_ERROR, 0, 0 ); - return _errorID; - } - - TIXML_FSEEK( fp, 0, SEEK_END ); - - unsigned long long filelength; - { - const long long fileLengthSigned = TIXML_FTELL( fp ); - TIXML_FSEEK( fp, 0, SEEK_SET ); - if ( fileLengthSigned == -1L ) { - SetError( XML_ERROR_FILE_READ_ERROR, 0, 0 ); - return _errorID; - } - TIXMLASSERT( fileLengthSigned >= 0 ); - filelength = static_cast(fileLengthSigned); - } - - const size_t maxSizeT = static_cast(-1); - // We'll do the comparison as an unsigned long long, because that's guaranteed to be at - // least 8 bytes, even on a 32-bit platform. - if ( filelength >= static_cast(maxSizeT) ) { - // Cannot handle files which won't fit in buffer together with null terminator - SetError( XML_ERROR_FILE_READ_ERROR, 0, 0 ); - return _errorID; - } - - if ( filelength == 0 ) { - SetError( XML_ERROR_EMPTY_DOCUMENT, 0, 0 ); - return _errorID; - } - - const size_t size = static_cast(filelength); - TIXMLASSERT( _charBuffer == 0 ); - _charBuffer = new char[size+1]; - const size_t read = fread( _charBuffer, 1, size, fp ); - if ( read != size ) { - SetError( XML_ERROR_FILE_READ_ERROR, 0, 0 ); - return _errorID; - } - - _charBuffer[size] = 0; - - Parse(); - return _errorID; -} - - -XMLError XMLDocument::SaveFile( const char* filename, bool compact ) -{ - if ( !filename ) { - TIXMLASSERT( false ); - SetError( XML_ERROR_FILE_COULD_NOT_BE_OPENED, 0, "filename=" ); - return _errorID; - } - - FILE* fp = callfopen( filename, "w" ); - if ( !fp ) { - SetError( XML_ERROR_FILE_COULD_NOT_BE_OPENED, 0, "filename=%s", filename ); - return _errorID; - } - SaveFile(fp, compact); - fclose( fp ); - return _errorID; -} - - -XMLError XMLDocument::SaveFile( FILE* fp, bool compact ) -{ - // Clear any error from the last save, otherwise it will get reported - // for *this* call. - ClearError(); - XMLPrinter stream( fp, compact ); - Print( &stream ); - return _errorID; -} - - -XMLError XMLDocument::Parse( const char* xml, size_t nBytes ) -{ - Clear(); - - if ( nBytes == 0 || !xml || !*xml ) { - SetError( XML_ERROR_EMPTY_DOCUMENT, 0, 0 ); - return _errorID; - } - if ( nBytes == static_cast(-1) ) { - nBytes = strlen( xml ); - } - TIXMLASSERT( _charBuffer == 0 ); - _charBuffer = new char[ nBytes+1 ]; - memcpy( _charBuffer, xml, nBytes ); - _charBuffer[nBytes] = 0; - - Parse(); - if ( Error() ) { - // clean up now essentially dangling memory. - // and the parse fail can put objects in the - // pools that are dead and inaccessible. - DeleteChildren(); - _elementPool.Clear(); - _attributePool.Clear(); - _textPool.Clear(); - _commentPool.Clear(); - } - return _errorID; -} - - -void XMLDocument::Print( XMLPrinter* streamer ) const -{ - if ( streamer ) { - Accept( streamer ); - } - else { - XMLPrinter stdoutStreamer( stdout ); - Accept( &stdoutStreamer ); - } -} - - -void XMLDocument::ClearError() { - _errorID = XML_SUCCESS; - _errorLineNum = 0; - _errorStr.Reset(); -} - - -void XMLDocument::SetError( XMLError error, int lineNum, const char* format, ... ) -{ - TIXMLASSERT(error >= 0 && error < XML_ERROR_COUNT); - _errorID = error; - _errorLineNum = lineNum; - _errorStr.Reset(); - - const size_t BUFFER_SIZE = 1000; - char* buffer = new char[BUFFER_SIZE]; - - TIXMLASSERT(sizeof(error) <= sizeof(int)); - TIXML_SNPRINTF(buffer, BUFFER_SIZE, "Error=%s ErrorID=%d (0x%x) Line number=%d", - ErrorIDToName(error), static_cast(error), static_cast(error), lineNum); - - if (format) { - size_t len = strlen(buffer); - TIXML_SNPRINTF(buffer + len, BUFFER_SIZE - len, ": "); - len = strlen(buffer); - - va_list va; - va_start(va, format); - TIXML_VSNPRINTF(buffer + len, BUFFER_SIZE - len, format, va); - va_end(va); - } - _errorStr.SetStr(buffer); - delete[] buffer; -} - - -/*static*/ const char* XMLDocument::ErrorIDToName(XMLError errorID) -{ - TIXMLASSERT( errorID >= 0 && errorID < XML_ERROR_COUNT ); - const char* errorName = _errorNames[errorID]; - TIXMLASSERT( errorName && errorName[0] ); - return errorName; -} - -const char* XMLDocument::ErrorStr() const -{ - return _errorStr.Empty() ? "" : _errorStr.GetStr(); -} - - -void XMLDocument::PrintError() const -{ - printf("%s\n", ErrorStr()); -} - -const char* XMLDocument::ErrorName() const -{ - return ErrorIDToName(_errorID); -} - -void XMLDocument::Parse() -{ - TIXMLASSERT( NoChildren() ); // Clear() must have been called previously - TIXMLASSERT( _charBuffer ); - _parseCurLineNum = 1; - _parseLineNum = 1; - char* p = _charBuffer; - p = XMLUtil::SkipWhiteSpace( p, &_parseCurLineNum ); - p = const_cast( XMLUtil::ReadBOM( p, &_writeBOM ) ); - if ( !*p ) { - SetError( XML_ERROR_EMPTY_DOCUMENT, 0, 0 ); - return; - } - ParseDeep(p, 0, &_parseCurLineNum ); -} - -void XMLDocument::PushDepth() -{ - _parsingDepth++; - if (_parsingDepth == TINYXML2_MAX_ELEMENT_DEPTH) { - SetError(XML_ELEMENT_DEPTH_EXCEEDED, _parseCurLineNum, "Element nesting is too deep." ); - } -} - -void XMLDocument::PopDepth() -{ - TIXMLASSERT(_parsingDepth > 0); - --_parsingDepth; -} - -XMLPrinter::XMLPrinter( FILE* file, bool compact, int depth ) : - _elementJustOpened( false ), - _stack(), - _firstElement( true ), - _fp( file ), - _depth( depth ), - _textDepth( -1 ), - _processEntities( true ), - _compactMode( compact ), - _buffer() -{ - for( int i=0; i(entityValue); - TIXMLASSERT( flagIndex < ENTITY_RANGE ); - _entityFlag[flagIndex] = true; - } - _restrictedEntityFlag[static_cast('&')] = true; - _restrictedEntityFlag[static_cast('<')] = true; - _restrictedEntityFlag[static_cast('>')] = true; // not required, but consistency is nice - _buffer.Push( 0 ); -} - - -void XMLPrinter::Print( const char* format, ... ) -{ - va_list va; - va_start( va, format ); - - if ( _fp ) { - vfprintf( _fp, format, va ); - } - else { - const int len = TIXML_VSCPRINTF( format, va ); - // Close out and re-start the va-args - va_end( va ); - TIXMLASSERT( len >= 0 ); - va_start( va, format ); - TIXMLASSERT( _buffer.Size() > 0 && _buffer[_buffer.Size() - 1] == 0 ); - char* p = _buffer.PushArr( len ) - 1; // back up over the null terminator. - TIXML_VSNPRINTF( p, len+1, format, va ); - } - va_end( va ); -} - - -void XMLPrinter::Write( const char* data, size_t size ) -{ - if ( _fp ) { - fwrite ( data , sizeof(char), size, _fp); - } - else { - char* p = _buffer.PushArr( static_cast(size) ) - 1; // back up over the null terminator. - memcpy( p, data, size ); - p[size] = 0; - } -} - - -void XMLPrinter::Putc( char ch ) -{ - if ( _fp ) { - fputc ( ch, _fp); - } - else { - char* p = _buffer.PushArr( sizeof(char) ) - 1; // back up over the null terminator. - p[0] = ch; - p[1] = 0; - } -} - - -void XMLPrinter::PrintSpace( int depth ) -{ - for( int i=0; i 0 && *q < ENTITY_RANGE ) { - // Check for entities. If one is found, flush - // the stream up until the entity, write the - // entity, and keep looking. - if ( flag[static_cast(*q)] ) { - while ( p < q ) { - const size_t delta = q - p; - const int toPrint = ( INT_MAX < delta ) ? INT_MAX : static_cast(delta); - Write( p, toPrint ); - p += toPrint; - } - bool entityPatternPrinted = false; - for( int i=0; i(delta); - Write( p, toPrint ); - } - } - else { - Write( p ); - } -} - - -void XMLPrinter::PushHeader( bool writeBOM, bool writeDec ) -{ - if ( writeBOM ) { - static const unsigned char bom[] = { TIXML_UTF_LEAD_0, TIXML_UTF_LEAD_1, TIXML_UTF_LEAD_2, 0 }; - Write( reinterpret_cast< const char* >( bom ) ); - } - if ( writeDec ) { - PushDeclaration( "xml version=\"1.0\"" ); - } -} - -void XMLPrinter::PrepareForNewNode( bool compactMode ) -{ - SealElementIfJustOpened(); - - if ( compactMode ) { - return; - } - - if ( _firstElement ) { - PrintSpace (_depth); - } else if ( _textDepth < 0) { - Putc( '\n' ); - PrintSpace( _depth ); - } - - _firstElement = false; -} - -void XMLPrinter::OpenElement( const char* name, bool compactMode ) -{ - PrepareForNewNode( compactMode ); - _stack.Push( name ); - - Write ( "<" ); - Write ( name ); - - _elementJustOpened = true; - ++_depth; -} - - -void XMLPrinter::PushAttribute( const char* name, const char* value ) -{ - TIXMLASSERT( _elementJustOpened ); - Putc ( ' ' ); - Write( name ); - Write( "=\"" ); - PrintString( value, false ); - Putc ( '\"' ); -} - - -void XMLPrinter::PushAttribute( const char* name, int v ) -{ - char buf[BUF_SIZE]; - XMLUtil::ToStr( v, buf, BUF_SIZE ); - PushAttribute( name, buf ); -} - - -void XMLPrinter::PushAttribute( const char* name, unsigned v ) -{ - char buf[BUF_SIZE]; - XMLUtil::ToStr( v, buf, BUF_SIZE ); - PushAttribute( name, buf ); -} - - -void XMLPrinter::PushAttribute(const char* name, int64_t v) -{ - char buf[BUF_SIZE]; - XMLUtil::ToStr(v, buf, BUF_SIZE); - PushAttribute(name, buf); -} - - -void XMLPrinter::PushAttribute(const char* name, uint64_t v) -{ - char buf[BUF_SIZE]; - XMLUtil::ToStr(v, buf, BUF_SIZE); - PushAttribute(name, buf); -} - - -void XMLPrinter::PushAttribute( const char* name, bool v ) -{ - char buf[BUF_SIZE]; - XMLUtil::ToStr( v, buf, BUF_SIZE ); - PushAttribute( name, buf ); -} - - -void XMLPrinter::PushAttribute( const char* name, double v ) -{ - char buf[BUF_SIZE]; - XMLUtil::ToStr( v, buf, BUF_SIZE ); - PushAttribute( name, buf ); -} - - -void XMLPrinter::CloseElement( bool compactMode ) -{ - --_depth; - const char* name = _stack.Pop(); - - if ( _elementJustOpened ) { - Write( "/>" ); - } - else { - if ( _textDepth < 0 && !compactMode) { - Putc( '\n' ); - PrintSpace( _depth ); - } - Write ( "" ); - } - - if ( _textDepth == _depth ) { - _textDepth = -1; - } - if ( _depth == 0 && !compactMode) { - Putc( '\n' ); - } - _elementJustOpened = false; -} - - -void XMLPrinter::SealElementIfJustOpened() -{ - if ( !_elementJustOpened ) { - return; - } - _elementJustOpened = false; - Putc( '>' ); -} - - -void XMLPrinter::PushText( const char* text, bool cdata ) -{ - _textDepth = _depth-1; - - SealElementIfJustOpened(); - if ( cdata ) { - Write( "" ); - } - else { - PrintString( text, true ); - } -} - - -void XMLPrinter::PushText( int64_t value ) -{ - char buf[BUF_SIZE]; - XMLUtil::ToStr( value, buf, BUF_SIZE ); - PushText( buf, false ); -} - - -void XMLPrinter::PushText( uint64_t value ) -{ - char buf[BUF_SIZE]; - XMLUtil::ToStr(value, buf, BUF_SIZE); - PushText(buf, false); -} - - -void XMLPrinter::PushText( int value ) -{ - char buf[BUF_SIZE]; - XMLUtil::ToStr( value, buf, BUF_SIZE ); - PushText( buf, false ); -} - - -void XMLPrinter::PushText( unsigned value ) -{ - char buf[BUF_SIZE]; - XMLUtil::ToStr( value, buf, BUF_SIZE ); - PushText( buf, false ); -} - - -void XMLPrinter::PushText( bool value ) -{ - char buf[BUF_SIZE]; - XMLUtil::ToStr( value, buf, BUF_SIZE ); - PushText( buf, false ); -} - - -void XMLPrinter::PushText( float value ) -{ - char buf[BUF_SIZE]; - XMLUtil::ToStr( value, buf, BUF_SIZE ); - PushText( buf, false ); -} - - -void XMLPrinter::PushText( double value ) -{ - char buf[BUF_SIZE]; - XMLUtil::ToStr( value, buf, BUF_SIZE ); - PushText( buf, false ); -} - - -void XMLPrinter::PushComment( const char* comment ) -{ - PrepareForNewNode( _compactMode ); - - Write( "" ); -} - - -void XMLPrinter::PushDeclaration( const char* value ) -{ - PrepareForNewNode( _compactMode ); - - Write( "" ); -} - - -void XMLPrinter::PushUnknown( const char* value ) -{ - PrepareForNewNode( _compactMode ); - - Write( "' ); -} - - -bool XMLPrinter::VisitEnter( const XMLDocument& doc ) -{ - _processEntities = doc.ProcessEntities(); - if ( doc.HasBOM() ) { - PushHeader( true, false ); - } - return true; -} - - -bool XMLPrinter::VisitEnter( const XMLElement& element, const XMLAttribute* attribute ) -{ - const XMLElement* parentElem = 0; - if ( element.Parent() ) { - parentElem = element.Parent()->ToElement(); - } - const bool compactMode = parentElem ? CompactMode( *parentElem ) : _compactMode; - OpenElement( element.Name(), compactMode ); - while ( attribute ) { - PushAttribute( attribute->Name(), attribute->Value() ); - attribute = attribute->Next(); - } - return true; -} - - -bool XMLPrinter::VisitExit( const XMLElement& element ) -{ - CloseElement( CompactMode(element) ); - return true; -} - - -bool XMLPrinter::Visit( const XMLText& text ) -{ - PushText( text.Value(), text.CData() ); - return true; -} - - -bool XMLPrinter::Visit( const XMLComment& comment ) -{ - PushComment( comment.Value() ); - return true; -} - -bool XMLPrinter::Visit( const XMLDeclaration& declaration ) -{ - PushDeclaration( declaration.Value() ); - return true; -} - - -bool XMLPrinter::Visit( const XMLUnknown& unknown ) -{ - PushUnknown( unknown.Value() ); - return true; -} - -} // namespace tinyxml2 diff --git a/src/xmutil/third_party/tinyxml2/tinyxml2.h b/src/xmutil/third_party/tinyxml2/tinyxml2.h deleted file mode 100755 index 9626be8a0..000000000 --- a/src/xmutil/third_party/tinyxml2/tinyxml2.h +++ /dev/null @@ -1,2383 +0,0 @@ -/* -Original code by Lee Thomason (www.grinninglizard.com) - -This software is provided 'as-is', without any express or implied -warranty. In no event will the authors be held liable for any -damages arising from the use of this software. - -Permission is granted to anyone to use this software for any -purpose, including commercial applications, and to alter it and -redistribute it freely, subject to the following restrictions: - -1. The origin of this software must not be misrepresented; you must -not claim that you wrote the original software. If you use this -software in a product, an acknowledgment in the product documentation -would be appreciated but is not required. - -2. Altered source versions must be plainly marked as such, and -must not be misrepresented as being the original software. - -3. This notice may not be removed or altered from any source -distribution. -*/ - -#ifndef TINYXML2_INCLUDED -#define TINYXML2_INCLUDED - -#if defined(ANDROID_NDK) || defined(__BORLANDC__) || defined(__QNXNTO__) -# include -# include -# include -# include -# include -# if defined(__PS3__) -# include -# endif -#else -# include -# include -# include -# include -# include -#endif -#include - -/* - gcc: - g++ -Wall -DTINYXML2_DEBUG tinyxml2.cpp xmltest.cpp -o gccxmltest.exe - - Formatting, Artistic Style: - AStyle.exe --style=1tbs --indent-switches --break-closing-brackets --indent-preprocessor tinyxml2.cpp tinyxml2.h -*/ - -#if defined( _DEBUG ) || defined (__DEBUG__) -# ifndef TINYXML2_DEBUG -# define TINYXML2_DEBUG -# endif -#endif - -#ifdef _MSC_VER -# pragma warning(push) -# pragma warning(disable: 4251) -#endif - -#ifdef _MSC_VER -# ifdef TINYXML2_EXPORT -# define TINYXML2_LIB __declspec(dllexport) -# elif defined(TINYXML2_IMPORT) -# define TINYXML2_LIB __declspec(dllimport) -# else -# define TINYXML2_LIB -# endif -#elif __GNUC__ >= 4 -# define TINYXML2_LIB __attribute__((visibility("default"))) -#else -# define TINYXML2_LIB -#endif - - -#if !defined(TIXMLASSERT) -#if defined(TINYXML2_DEBUG) -# if defined(_MSC_VER) -# // "(void)0," is for suppressing C4127 warning in "assert(false)", "assert(true)" and the like -# define TIXMLASSERT( x ) do { if ( !((void)0,(x))) { __debugbreak(); } } while(false) -# elif defined (ANDROID_NDK) -# include -# define TIXMLASSERT( x ) do { if ( !(x)) { __android_log_assert( "assert", "grinliz", "ASSERT in '%s' at %d.", __FILE__, __LINE__ ); } } while(false) -# else -# include -# define TIXMLASSERT assert -# endif -#else -# define TIXMLASSERT( x ) do {} while(false) -#endif -#endif - -/* Versioning, past 1.0.14: - http://semver.org/ -*/ -static const int TIXML2_MAJOR_VERSION = 11; -static const int TIXML2_MINOR_VERSION = 0; -static const int TIXML2_PATCH_VERSION = 0; - -#define TINYXML2_MAJOR_VERSION 11 -#define TINYXML2_MINOR_VERSION 0 -#define TINYXML2_PATCH_VERSION 0 - -// A fixed element depth limit is problematic. There needs to be a -// limit to avoid a stack overflow. However, that limit varies per -// system, and the capacity of the stack. On the other hand, it's a trivial -// attack that can result from ill, malicious, or even correctly formed XML, -// so there needs to be a limit in place. -static const int TINYXML2_MAX_ELEMENT_DEPTH = 500; - -namespace tinyxml2 -{ -class XMLDocument; -class XMLElement; -class XMLAttribute; -class XMLComment; -class XMLText; -class XMLDeclaration; -class XMLUnknown; -class XMLPrinter; - -/* - A class that wraps strings. Normally stores the start and end - pointers into the XML file itself, and will apply normalization - and entity translation if actually read. Can also store (and memory - manage) a traditional char[] - - Isn't clear why TINYXML2_LIB is needed; but seems to fix #719 -*/ -class TINYXML2_LIB StrPair -{ -public: - enum Mode { - NEEDS_ENTITY_PROCESSING = 0x01, - NEEDS_NEWLINE_NORMALIZATION = 0x02, - NEEDS_WHITESPACE_COLLAPSING = 0x04, - - TEXT_ELEMENT = NEEDS_ENTITY_PROCESSING | NEEDS_NEWLINE_NORMALIZATION, - TEXT_ELEMENT_LEAVE_ENTITIES = NEEDS_NEWLINE_NORMALIZATION, - ATTRIBUTE_NAME = 0, - ATTRIBUTE_VALUE = NEEDS_ENTITY_PROCESSING | NEEDS_NEWLINE_NORMALIZATION, - ATTRIBUTE_VALUE_LEAVE_ENTITIES = NEEDS_NEWLINE_NORMALIZATION, - COMMENT = NEEDS_NEWLINE_NORMALIZATION - }; - - StrPair() : _flags( 0 ), _start( 0 ), _end( 0 ) {} - ~StrPair(); - - void Set( char* start, char* end, int flags ) { - TIXMLASSERT( start ); - TIXMLASSERT( end ); - Reset(); - _start = start; - _end = end; - _flags = flags | NEEDS_FLUSH; - } - - const char* GetStr(); - - bool Empty() const { - return _start == _end; - } - - void SetInternedStr( const char* str ) { - Reset(); - _start = const_cast(str); - } - - void SetStr( const char* str, int flags=0 ); - - char* ParseText( char* in, const char* endTag, int strFlags, int* curLineNumPtr ); - char* ParseName( char* in ); - - void TransferTo( StrPair* other ); - void Reset(); - -private: - void CollapseWhitespace(); - - enum { - NEEDS_FLUSH = 0x100, - NEEDS_DELETE = 0x200 - }; - - int _flags; - char* _start; - char* _end; - - StrPair( const StrPair& other ); // not supported - void operator=( const StrPair& other ); // not supported, use TransferTo() -}; - - -/* - A dynamic array of Plain Old Data. Doesn't support constructors, etc. - Has a small initial memory pool, so that low or no usage will not - cause a call to new/delete -*/ -template -class DynArray -{ -public: - DynArray() : - _mem( _pool ), - _allocated( INITIAL_SIZE ), - _size( 0 ) - { - } - - ~DynArray() { - if ( _mem != _pool ) { - delete [] _mem; - } - } - - void Clear() { - _size = 0; - } - - void Push( T t ) { - TIXMLASSERT( _size < INT_MAX ); - EnsureCapacity( _size+1 ); - _mem[_size] = t; - ++_size; - } - - T* PushArr( size_t count ) { - TIXMLASSERT( _size <= SIZE_MAX - count ); - EnsureCapacity( _size+count ); - T* ret = &_mem[_size]; - _size += count; - return ret; - } - - T Pop() { - TIXMLASSERT( _size > 0 ); - --_size; - return _mem[_size]; - } - - void PopArr( size_t count ) { - TIXMLASSERT( _size >= count ); - _size -= count; - } - - bool Empty() const { - return _size == 0; - } - - T& operator[](size_t i) { - TIXMLASSERT( i < _size ); - return _mem[i]; - } - - const T& operator[](size_t i) const { - TIXMLASSERT( i < _size ); - return _mem[i]; - } - - const T& PeekTop() const { - TIXMLASSERT( _size > 0 ); - return _mem[ _size - 1]; - } - - size_t Size() const { - TIXMLASSERT( _size >= 0 ); - return _size; - } - - size_t Capacity() const { - TIXMLASSERT( _allocated >= INITIAL_SIZE ); - return _allocated; - } - - void SwapRemove(size_t i) { - TIXMLASSERT(i < _size); - TIXMLASSERT(_size > 0); - _mem[i] = _mem[_size - 1]; - --_size; - } - - const T* Mem() const { - TIXMLASSERT( _mem ); - return _mem; - } - - T* Mem() { - TIXMLASSERT( _mem ); - return _mem; - } - -private: - DynArray( const DynArray& ); // not supported - void operator=( const DynArray& ); // not supported - - void EnsureCapacity( size_t cap ) { - TIXMLASSERT( cap > 0 ); - if ( cap > _allocated ) { - TIXMLASSERT( cap <= SIZE_MAX / 2 / sizeof(T)); - const size_t newAllocated = cap * 2; - T* newMem = new T[newAllocated]; - TIXMLASSERT( newAllocated >= _size ); - memcpy( newMem, _mem, sizeof(T) * _size ); // warning: not using constructors, only works for PODs - if ( _mem != _pool ) { - delete [] _mem; - } - _mem = newMem; - _allocated = newAllocated; - } - } - - T* _mem; - T _pool[INITIAL_SIZE]; - size_t _allocated; // objects allocated - size_t _size; // number objects in use -}; - - -/* - Parent virtual class of a pool for fast allocation - and deallocation of objects. -*/ -class MemPool -{ -public: - MemPool() {} - virtual ~MemPool() {} - - virtual size_t ItemSize() const = 0; - virtual void* Alloc() = 0; - virtual void Free( void* ) = 0; - virtual void SetTracked() = 0; -}; - - -/* - Template child class to create pools of the correct type. -*/ -template< size_t ITEM_SIZE > -class MemPoolT : public MemPool -{ -public: - MemPoolT() : _blockPtrs(), _root(0), _currentAllocs(0), _nAllocs(0), _maxAllocs(0), _nUntracked(0) {} - ~MemPoolT() { - MemPoolT< ITEM_SIZE >::Clear(); - } - - void Clear() { - // Delete the blocks. - while( !_blockPtrs.Empty()) { - Block* lastBlock = _blockPtrs.Pop(); - delete lastBlock; - } - _root = 0; - _currentAllocs = 0; - _nAllocs = 0; - _maxAllocs = 0; - _nUntracked = 0; - } - - virtual size_t ItemSize() const override { - return ITEM_SIZE; - } - size_t CurrentAllocs() const { - return _currentAllocs; - } - - virtual void* Alloc() override{ - if ( !_root ) { - // Need a new block. - Block* block = new Block; - _blockPtrs.Push( block ); - - Item* blockItems = block->items; - for( size_t i = 0; i < ITEMS_PER_BLOCK - 1; ++i ) { - blockItems[i].next = &(blockItems[i + 1]); - } - blockItems[ITEMS_PER_BLOCK - 1].next = 0; - _root = blockItems; - } - Item* const result = _root; - TIXMLASSERT( result != 0 ); - _root = _root->next; - - ++_currentAllocs; - if ( _currentAllocs > _maxAllocs ) { - _maxAllocs = _currentAllocs; - } - ++_nAllocs; - ++_nUntracked; - return result; - } - - virtual void Free( void* mem ) override { - if ( !mem ) { - return; - } - --_currentAllocs; - Item* item = static_cast( mem ); -#ifdef TINYXML2_DEBUG - memset( item, 0xfe, sizeof( *item ) ); -#endif - item->next = _root; - _root = item; - } - void Trace( const char* name ) { - printf( "Mempool %s watermark=%d [%dk] current=%d size=%d nAlloc=%d blocks=%d\n", - name, _maxAllocs, _maxAllocs * ITEM_SIZE / 1024, _currentAllocs, - ITEM_SIZE, _nAllocs, _blockPtrs.Size() ); - } - - void SetTracked() override { - --_nUntracked; - } - - size_t Untracked() const { - return _nUntracked; - } - - // This number is perf sensitive. 4k seems like a good tradeoff on my machine. - // The test file is large, 170k. - // Release: VS2010 gcc(no opt) - // 1k: 4000 - // 2k: 4000 - // 4k: 3900 21000 - // 16k: 5200 - // 32k: 4300 - // 64k: 4000 21000 - // Declared public because some compilers do not accept to use ITEMS_PER_BLOCK - // in private part if ITEMS_PER_BLOCK is private - enum { ITEMS_PER_BLOCK = (4 * 1024) / ITEM_SIZE }; - -private: - MemPoolT( const MemPoolT& ); // not supported - void operator=( const MemPoolT& ); // not supported - - union Item { - Item* next; - char itemData[static_cast(ITEM_SIZE)]; - }; - struct Block { - Item items[ITEMS_PER_BLOCK]; - }; - DynArray< Block*, 10 > _blockPtrs; - Item* _root; - - size_t _currentAllocs; - size_t _nAllocs; - size_t _maxAllocs; - size_t _nUntracked; -}; - - - -/** - Implements the interface to the "Visitor pattern" (see the Accept() method.) - If you call the Accept() method, it requires being passed a XMLVisitor - class to handle callbacks. For nodes that contain other nodes (Document, Element) - you will get called with a VisitEnter/VisitExit pair. Nodes that are always leafs - are simply called with Visit(). - - If you return 'true' from a Visit method, recursive parsing will continue. If you return - false, no children of this node or its siblings will be visited. - - All flavors of Visit methods have a default implementation that returns 'true' (continue - visiting). You need to only override methods that are interesting to you. - - Generally Accept() is called on the XMLDocument, although all nodes support visiting. - - You should never change the document from a callback. - - @sa XMLNode::Accept() -*/ -class TINYXML2_LIB XMLVisitor -{ -public: - virtual ~XMLVisitor() {} - - /// Visit a document. - virtual bool VisitEnter( const XMLDocument& /*doc*/ ) { - return true; - } - /// Visit a document. - virtual bool VisitExit( const XMLDocument& /*doc*/ ) { - return true; - } - - /// Visit an element. - virtual bool VisitEnter( const XMLElement& /*element*/, const XMLAttribute* /*firstAttribute*/ ) { - return true; - } - /// Visit an element. - virtual bool VisitExit( const XMLElement& /*element*/ ) { - return true; - } - - /// Visit a declaration. - virtual bool Visit( const XMLDeclaration& /*declaration*/ ) { - return true; - } - /// Visit a text node. - virtual bool Visit( const XMLText& /*text*/ ) { - return true; - } - /// Visit a comment node. - virtual bool Visit( const XMLComment& /*comment*/ ) { - return true; - } - /// Visit an unknown node. - virtual bool Visit( const XMLUnknown& /*unknown*/ ) { - return true; - } -}; - -// WARNING: must match XMLDocument::_errorNames[] -enum XMLError { - XML_SUCCESS = 0, - XML_NO_ATTRIBUTE, - XML_WRONG_ATTRIBUTE_TYPE, - XML_ERROR_FILE_NOT_FOUND, - XML_ERROR_FILE_COULD_NOT_BE_OPENED, - XML_ERROR_FILE_READ_ERROR, - XML_ERROR_PARSING_ELEMENT, - XML_ERROR_PARSING_ATTRIBUTE, - XML_ERROR_PARSING_TEXT, - XML_ERROR_PARSING_CDATA, - XML_ERROR_PARSING_COMMENT, - XML_ERROR_PARSING_DECLARATION, - XML_ERROR_PARSING_UNKNOWN, - XML_ERROR_EMPTY_DOCUMENT, - XML_ERROR_MISMATCHED_ELEMENT, - XML_ERROR_PARSING, - XML_CAN_NOT_CONVERT_TEXT, - XML_NO_TEXT_NODE, - XML_ELEMENT_DEPTH_EXCEEDED, - - XML_ERROR_COUNT -}; - - -/* - Utility functionality. -*/ -class TINYXML2_LIB XMLUtil -{ -public: - static const char* SkipWhiteSpace( const char* p, int* curLineNumPtr ) { - TIXMLASSERT( p ); - - while( IsWhiteSpace(*p) ) { - if (curLineNumPtr && *p == '\n') { - ++(*curLineNumPtr); - } - ++p; - } - TIXMLASSERT( p ); - return p; - } - static char* SkipWhiteSpace( char* const p, int* curLineNumPtr ) { - return const_cast( SkipWhiteSpace( const_cast(p), curLineNumPtr ) ); - } - - // Anything in the high order range of UTF-8 is assumed to not be whitespace. This isn't - // correct, but simple, and usually works. - static bool IsWhiteSpace( char p ) { - return !IsUTF8Continuation(p) && isspace( static_cast(p) ); - } - - inline static bool IsNameStartChar( unsigned char ch ) { - if ( ch >= 128 ) { - // This is a heuristic guess in attempt to not implement Unicode-aware isalpha() - return true; - } - if ( isalpha( ch ) ) { - return true; - } - return ch == ':' || ch == '_'; - } - - inline static bool IsNameChar( unsigned char ch ) { - return IsNameStartChar( ch ) - || isdigit( ch ) - || ch == '.' - || ch == '-'; - } - - inline static bool IsPrefixHex( const char* p) { - p = SkipWhiteSpace(p, 0); - return p && *p == '0' && ( *(p + 1) == 'x' || *(p + 1) == 'X'); - } - - inline static bool StringEqual( const char* p, const char* q, int nChar=INT_MAX ) { - if ( p == q ) { - return true; - } - TIXMLASSERT( p ); - TIXMLASSERT( q ); - TIXMLASSERT( nChar >= 0 ); - return strncmp( p, q, static_cast(nChar) ) == 0; - } - - inline static bool IsUTF8Continuation( const char p ) { - return ( p & 0x80 ) != 0; - } - - static const char* ReadBOM( const char* p, bool* hasBOM ); - // p is the starting location, - // the UTF-8 value of the entity will be placed in value, and length filled in. - static const char* GetCharacterRef( const char* p, char* value, int* length ); - static void ConvertUTF32ToUTF8( unsigned long input, char* output, int* length ); - - // converts primitive types to strings - static void ToStr( int v, char* buffer, int bufferSize ); - static void ToStr( unsigned v, char* buffer, int bufferSize ); - static void ToStr( bool v, char* buffer, int bufferSize ); - static void ToStr( float v, char* buffer, int bufferSize ); - static void ToStr( double v, char* buffer, int bufferSize ); - static void ToStr(int64_t v, char* buffer, int bufferSize); - static void ToStr(uint64_t v, char* buffer, int bufferSize); - - // converts strings to primitive types - static bool ToInt( const char* str, int* value ); - static bool ToUnsigned( const char* str, unsigned* value ); - static bool ToBool( const char* str, bool* value ); - static bool ToFloat( const char* str, float* value ); - static bool ToDouble( const char* str, double* value ); - static bool ToInt64(const char* str, int64_t* value); - static bool ToUnsigned64(const char* str, uint64_t* value); - // Changes what is serialized for a boolean value. - // Default to "true" and "false". Shouldn't be changed - // unless you have a special testing or compatibility need. - // Be careful: static, global, & not thread safe. - // Be sure to set static const memory as parameters. - static void SetBoolSerialization(const char* writeTrue, const char* writeFalse); - -private: - static const char* writeBoolTrue; - static const char* writeBoolFalse; -}; - - -/** XMLNode is a base class for every object that is in the - XML Document Object Model (DOM), except XMLAttributes. - Nodes have siblings, a parent, and children which can - be navigated. A node is always in a XMLDocument. - The type of a XMLNode can be queried, and it can - be cast to its more defined type. - - A XMLDocument allocates memory for all its Nodes. - When the XMLDocument gets deleted, all its Nodes - will also be deleted. - - @verbatim - A Document can contain: Element (container or leaf) - Comment (leaf) - Unknown (leaf) - Declaration( leaf ) - - An Element can contain: Element (container or leaf) - Text (leaf) - Attributes (not on tree) - Comment (leaf) - Unknown (leaf) - - @endverbatim -*/ -class TINYXML2_LIB XMLNode -{ - friend class XMLDocument; - friend class XMLElement; -public: - - /// Get the XMLDocument that owns this XMLNode. - const XMLDocument* GetDocument() const { - TIXMLASSERT( _document ); - return _document; - } - /// Get the XMLDocument that owns this XMLNode. - XMLDocument* GetDocument() { - TIXMLASSERT( _document ); - return _document; - } - - /// Safely cast to an Element, or null. - virtual XMLElement* ToElement() { - return 0; - } - /// Safely cast to Text, or null. - virtual XMLText* ToText() { - return 0; - } - /// Safely cast to a Comment, or null. - virtual XMLComment* ToComment() { - return 0; - } - /// Safely cast to a Document, or null. - virtual XMLDocument* ToDocument() { - return 0; - } - /// Safely cast to a Declaration, or null. - virtual XMLDeclaration* ToDeclaration() { - return 0; - } - /// Safely cast to an Unknown, or null. - virtual XMLUnknown* ToUnknown() { - return 0; - } - - virtual const XMLElement* ToElement() const { - return 0; - } - virtual const XMLText* ToText() const { - return 0; - } - virtual const XMLComment* ToComment() const { - return 0; - } - virtual const XMLDocument* ToDocument() const { - return 0; - } - virtual const XMLDeclaration* ToDeclaration() const { - return 0; - } - virtual const XMLUnknown* ToUnknown() const { - return 0; - } - - // ChildElementCount was originally suggested by msteiger on the sourceforge page for TinyXML and modified by KB1SPH for TinyXML-2. - - int ChildElementCount(const char *value) const; - - int ChildElementCount() const; - - /** The meaning of 'value' changes for the specific type. - @verbatim - Document: empty (NULL is returned, not an empty string) - Element: name of the element - Comment: the comment text - Unknown: the tag contents - Text: the text string - @endverbatim - */ - const char* Value() const; - - /** Set the Value of an XML node. - @sa Value() - */ - void SetValue( const char* val, bool staticMem=false ); - - /// Gets the line number the node is in, if the document was parsed from a file. - int GetLineNum() const { return _parseLineNum; } - - /// Get the parent of this node on the DOM. - const XMLNode* Parent() const { - return _parent; - } - - XMLNode* Parent() { - return _parent; - } - - /// Returns true if this node has no children. - bool NoChildren() const { - return !_firstChild; - } - - /// Get the first child node, or null if none exists. - const XMLNode* FirstChild() const { - return _firstChild; - } - - XMLNode* FirstChild() { - return _firstChild; - } - - /** Get the first child element, or optionally the first child - element with the specified name. - */ - const XMLElement* FirstChildElement( const char* name = 0 ) const; - - XMLElement* FirstChildElement( const char* name = 0 ) { - return const_cast(const_cast(this)->FirstChildElement( name )); - } - - /// Get the last child node, or null if none exists. - const XMLNode* LastChild() const { - return _lastChild; - } - - XMLNode* LastChild() { - return _lastChild; - } - - /** Get the last child element or optionally the last child - element with the specified name. - */ - const XMLElement* LastChildElement( const char* name = 0 ) const; - - XMLElement* LastChildElement( const char* name = 0 ) { - return const_cast(const_cast(this)->LastChildElement(name) ); - } - - /// Get the previous (left) sibling node of this node. - const XMLNode* PreviousSibling() const { - return _prev; - } - - XMLNode* PreviousSibling() { - return _prev; - } - - /// Get the previous (left) sibling element of this node, with an optionally supplied name. - const XMLElement* PreviousSiblingElement( const char* name = 0 ) const ; - - XMLElement* PreviousSiblingElement( const char* name = 0 ) { - return const_cast(const_cast(this)->PreviousSiblingElement( name ) ); - } - - /// Get the next (right) sibling node of this node. - const XMLNode* NextSibling() const { - return _next; - } - - XMLNode* NextSibling() { - return _next; - } - - /// Get the next (right) sibling element of this node, with an optionally supplied name. - const XMLElement* NextSiblingElement( const char* name = 0 ) const; - - XMLElement* NextSiblingElement( const char* name = 0 ) { - return const_cast(const_cast(this)->NextSiblingElement( name ) ); - } - - /** - Add a child node as the last (right) child. - If the child node is already part of the document, - it is moved from its old location to the new location. - Returns the addThis argument or 0 if the node does not - belong to the same document. - */ - XMLNode* InsertEndChild( XMLNode* addThis ); - - XMLNode* LinkEndChild( XMLNode* addThis ) { - return InsertEndChild( addThis ); - } - /** - Add a child node as the first (left) child. - If the child node is already part of the document, - it is moved from its old location to the new location. - Returns the addThis argument or 0 if the node does not - belong to the same document. - */ - XMLNode* InsertFirstChild( XMLNode* addThis ); - /** - Add a node after the specified child node. - If the child node is already part of the document, - it is moved from its old location to the new location. - Returns the addThis argument or 0 if the afterThis node - is not a child of this node, or if the node does not - belong to the same document. - */ - XMLNode* InsertAfterChild( XMLNode* afterThis, XMLNode* addThis ); - - /** - Delete all the children of this node. - */ - void DeleteChildren(); - - /** - Delete a child of this node. - */ - void DeleteChild( XMLNode* node ); - - /** - Make a copy of this node, but not its children. - You may pass in a Document pointer that will be - the owner of the new Node. If the 'document' is - null, then the node returned will be allocated - from the current Document. (this->GetDocument()) - - Note: if called on a XMLDocument, this will return null. - */ - virtual XMLNode* ShallowClone( XMLDocument* document ) const = 0; - - /** - Make a copy of this node and all its children. - - If the 'target' is null, then the nodes will - be allocated in the current document. If 'target' - is specified, the memory will be allocated in the - specified XMLDocument. - - NOTE: This is probably not the correct tool to - copy a document, since XMLDocuments can have multiple - top level XMLNodes. You probably want to use - XMLDocument::DeepCopy() - */ - XMLNode* DeepClone( XMLDocument* target ) const; - - /** - Test if 2 nodes are the same, but don't test children. - The 2 nodes do not need to be in the same Document. - - Note: if called on a XMLDocument, this will return false. - */ - virtual bool ShallowEqual( const XMLNode* compare ) const = 0; - - /** Accept a hierarchical visit of the nodes in the TinyXML-2 DOM. Every node in the - XML tree will be conditionally visited and the host will be called back - via the XMLVisitor interface. - - This is essentially a SAX interface for TinyXML-2. (Note however it doesn't re-parse - the XML for the callbacks, so the performance of TinyXML-2 is unchanged by using this - interface versus any other.) - - The interface has been based on ideas from: - - - http://www.saxproject.org/ - - http://c2.com/cgi/wiki?HierarchicalVisitorPattern - - Which are both good references for "visiting". - - An example of using Accept(): - @verbatim - XMLPrinter printer; - tinyxmlDoc.Accept( &printer ); - const char* xmlcstr = printer.CStr(); - @endverbatim - */ - virtual bool Accept( XMLVisitor* visitor ) const = 0; - - /** - Set user data into the XMLNode. TinyXML-2 in - no way processes or interprets user data. - It is initially 0. - */ - void SetUserData(void* userData) { _userData = userData; } - - /** - Get user data set into the XMLNode. TinyXML-2 in - no way processes or interprets user data. - It is initially 0. - */ - void* GetUserData() const { return _userData; } - -protected: - explicit XMLNode( XMLDocument* ); - virtual ~XMLNode(); - - virtual char* ParseDeep( char* p, StrPair* parentEndTag, int* curLineNumPtr); - - XMLDocument* _document; - XMLNode* _parent; - mutable StrPair _value; - int _parseLineNum; - - XMLNode* _firstChild; - XMLNode* _lastChild; - - XMLNode* _prev; - XMLNode* _next; - - void* _userData; - -private: - MemPool* _memPool; - void Unlink( XMLNode* child ); - static void DeleteNode( XMLNode* node ); - void InsertChildPreamble( XMLNode* insertThis ) const; - const XMLElement* ToElementWithName( const char* name ) const; - - XMLNode( const XMLNode& ); // not supported - XMLNode& operator=( const XMLNode& ); // not supported -}; - - -/** XML text. - - Note that a text node can have child element nodes, for example: - @verbatim - This is bold - @endverbatim - - A text node can have 2 ways to output the next. "normal" output - and CDATA. It will default to the mode it was parsed from the XML file and - you generally want to leave it alone, but you can change the output mode with - SetCData() and query it with CData(). -*/ -class TINYXML2_LIB XMLText : public XMLNode -{ - friend class XMLDocument; -public: - virtual bool Accept( XMLVisitor* visitor ) const override; - - virtual XMLText* ToText() override { - return this; - } - virtual const XMLText* ToText() const override { - return this; - } - - /// Declare whether this should be CDATA or standard text. - void SetCData( bool isCData ) { - _isCData = isCData; - } - /// Returns true if this is a CDATA text element. - bool CData() const { - return _isCData; - } - - virtual XMLNode* ShallowClone( XMLDocument* document ) const override; - virtual bool ShallowEqual( const XMLNode* compare ) const override; - -protected: - explicit XMLText( XMLDocument* doc ) : XMLNode( doc ), _isCData( false ) {} - virtual ~XMLText() {} - - char* ParseDeep( char* p, StrPair* parentEndTag, int* curLineNumPtr ) override; - -private: - bool _isCData; - - XMLText( const XMLText& ); // not supported - XMLText& operator=( const XMLText& ); // not supported -}; - - -/** An XML Comment. */ -class TINYXML2_LIB XMLComment : public XMLNode -{ - friend class XMLDocument; -public: - virtual XMLComment* ToComment() override { - return this; - } - virtual const XMLComment* ToComment() const override { - return this; - } - - virtual bool Accept( XMLVisitor* visitor ) const override; - - virtual XMLNode* ShallowClone( XMLDocument* document ) const override; - virtual bool ShallowEqual( const XMLNode* compare ) const override; - -protected: - explicit XMLComment( XMLDocument* doc ); - virtual ~XMLComment(); - - char* ParseDeep( char* p, StrPair* parentEndTag, int* curLineNumPtr) override; - -private: - XMLComment( const XMLComment& ); // not supported - XMLComment& operator=( const XMLComment& ); // not supported -}; - - -/** In correct XML the declaration is the first entry in the file. - @verbatim - - @endverbatim - - TinyXML-2 will happily read or write files without a declaration, - however. - - The text of the declaration isn't interpreted. It is parsed - and written as a string. -*/ -class TINYXML2_LIB XMLDeclaration : public XMLNode -{ - friend class XMLDocument; -public: - virtual XMLDeclaration* ToDeclaration() override { - return this; - } - virtual const XMLDeclaration* ToDeclaration() const override { - return this; - } - - virtual bool Accept( XMLVisitor* visitor ) const override; - - virtual XMLNode* ShallowClone( XMLDocument* document ) const override; - virtual bool ShallowEqual( const XMLNode* compare ) const override; - -protected: - explicit XMLDeclaration( XMLDocument* doc ); - virtual ~XMLDeclaration(); - - char* ParseDeep( char* p, StrPair* parentEndTag, int* curLineNumPtr ) override; - -private: - XMLDeclaration( const XMLDeclaration& ); // not supported - XMLDeclaration& operator=( const XMLDeclaration& ); // not supported -}; - - -/** Any tag that TinyXML-2 doesn't recognize is saved as an - unknown. It is a tag of text, but should not be modified. - It will be written back to the XML, unchanged, when the file - is saved. - - DTD tags get thrown into XMLUnknowns. -*/ -class TINYXML2_LIB XMLUnknown : public XMLNode -{ - friend class XMLDocument; -public: - virtual XMLUnknown* ToUnknown() override { - return this; - } - virtual const XMLUnknown* ToUnknown() const override { - return this; - } - - virtual bool Accept( XMLVisitor* visitor ) const override; - - virtual XMLNode* ShallowClone( XMLDocument* document ) const override; - virtual bool ShallowEqual( const XMLNode* compare ) const override; - -protected: - explicit XMLUnknown( XMLDocument* doc ); - virtual ~XMLUnknown(); - - char* ParseDeep( char* p, StrPair* parentEndTag, int* curLineNumPtr ) override; - -private: - XMLUnknown( const XMLUnknown& ); // not supported - XMLUnknown& operator=( const XMLUnknown& ); // not supported -}; - - - -/** An attribute is a name-value pair. Elements have an arbitrary - number of attributes, each with a unique name. - - @note The attributes are not XMLNodes. You may only query the - Next() attribute in a list. -*/ -class TINYXML2_LIB XMLAttribute -{ - friend class XMLElement; -public: - /// The name of the attribute. - const char* Name() const; - - /// The value of the attribute. - const char* Value() const; - - /// Gets the line number the attribute is in, if the document was parsed from a file. - int GetLineNum() const { return _parseLineNum; } - - /// The next attribute in the list. - const XMLAttribute* Next() const { - return _next; - } - - /** IntValue interprets the attribute as an integer, and returns the value. - If the value isn't an integer, 0 will be returned. There is no error checking; - use QueryIntValue() if you need error checking. - */ - int IntValue() const { - int i = 0; - QueryIntValue(&i); - return i; - } - - int64_t Int64Value() const { - int64_t i = 0; - QueryInt64Value(&i); - return i; - } - - uint64_t Unsigned64Value() const { - uint64_t i = 0; - QueryUnsigned64Value(&i); - return i; - } - - /// Query as an unsigned integer. See IntValue() - unsigned UnsignedValue() const { - unsigned i=0; - QueryUnsignedValue( &i ); - return i; - } - /// Query as a boolean. See IntValue() - bool BoolValue() const { - bool b=false; - QueryBoolValue( &b ); - return b; - } - /// Query as a double. See IntValue() - double DoubleValue() const { - double d=0; - QueryDoubleValue( &d ); - return d; - } - /// Query as a float. See IntValue() - float FloatValue() const { - float f=0; - QueryFloatValue( &f ); - return f; - } - - /** QueryIntValue interprets the attribute as an integer, and returns the value - in the provided parameter. The function will return XML_SUCCESS on success, - and XML_WRONG_ATTRIBUTE_TYPE if the conversion is not successful. - */ - XMLError QueryIntValue( int* value ) const; - /// See QueryIntValue - XMLError QueryUnsignedValue( unsigned int* value ) const; - /// See QueryIntValue - XMLError QueryInt64Value(int64_t* value) const; - /// See QueryIntValue - XMLError QueryUnsigned64Value(uint64_t* value) const; - /// See QueryIntValue - XMLError QueryBoolValue( bool* value ) const; - /// See QueryIntValue - XMLError QueryDoubleValue( double* value ) const; - /// See QueryIntValue - XMLError QueryFloatValue( float* value ) const; - - /// Set the attribute to a string value. - void SetAttribute( const char* value ); - /// Set the attribute to value. - void SetAttribute( int value ); - /// Set the attribute to value. - void SetAttribute( unsigned value ); - /// Set the attribute to value. - void SetAttribute(int64_t value); - /// Set the attribute to value. - void SetAttribute(uint64_t value); - /// Set the attribute to value. - void SetAttribute( bool value ); - /// Set the attribute to value. - void SetAttribute( double value ); - /// Set the attribute to value. - void SetAttribute( float value ); - -private: - enum { BUF_SIZE = 200 }; - - XMLAttribute() : _name(), _value(),_parseLineNum( 0 ), _next( 0 ), _memPool( 0 ) {} - virtual ~XMLAttribute() {} - - XMLAttribute( const XMLAttribute& ); // not supported - void operator=( const XMLAttribute& ); // not supported - void SetName( const char* name ); - - char* ParseDeep( char* p, bool processEntities, int* curLineNumPtr ); - - mutable StrPair _name; - mutable StrPair _value; - int _parseLineNum; - XMLAttribute* _next; - MemPool* _memPool; -}; - - -/** The element is a container class. It has a value, the element name, - and can contain other elements, text, comments, and unknowns. - Elements also contain an arbitrary number of attributes. -*/ -class TINYXML2_LIB XMLElement : public XMLNode -{ - friend class XMLDocument; -public: - /// Get the name of an element (which is the Value() of the node.) - const char* Name() const { - return Value(); - } - /// Set the name of the element. - void SetName( const char* str, bool staticMem=false ) { - SetValue( str, staticMem ); - } - - virtual XMLElement* ToElement() override { - return this; - } - virtual const XMLElement* ToElement() const override { - return this; - } - virtual bool Accept( XMLVisitor* visitor ) const override; - - /** Given an attribute name, Attribute() returns the value - for the attribute of that name, or null if none - exists. For example: - - @verbatim - const char* value = ele->Attribute( "foo" ); - @endverbatim - - The 'value' parameter is normally null. However, if specified, - the attribute will only be returned if the 'name' and 'value' - match. This allow you to write code: - - @verbatim - if ( ele->Attribute( "foo", "bar" ) ) callFooIsBar(); - @endverbatim - - rather than: - @verbatim - if ( ele->Attribute( "foo" ) ) { - if ( strcmp( ele->Attribute( "foo" ), "bar" ) == 0 ) callFooIsBar(); - } - @endverbatim - */ - const char* Attribute( const char* name, const char* value=0 ) const; - - /** Given an attribute name, IntAttribute() returns the value - of the attribute interpreted as an integer. The default - value will be returned if the attribute isn't present, - or if there is an error. (For a method with error - checking, see QueryIntAttribute()). - */ - int IntAttribute(const char* name, int defaultValue = 0) const; - /// See IntAttribute() - unsigned UnsignedAttribute(const char* name, unsigned defaultValue = 0) const; - /// See IntAttribute() - int64_t Int64Attribute(const char* name, int64_t defaultValue = 0) const; - /// See IntAttribute() - uint64_t Unsigned64Attribute(const char* name, uint64_t defaultValue = 0) const; - /// See IntAttribute() - bool BoolAttribute(const char* name, bool defaultValue = false) const; - /// See IntAttribute() - double DoubleAttribute(const char* name, double defaultValue = 0) const; - /// See IntAttribute() - float FloatAttribute(const char* name, float defaultValue = 0) const; - - /** Given an attribute name, QueryIntAttribute() returns - XML_SUCCESS, XML_WRONG_ATTRIBUTE_TYPE if the conversion - can't be performed, or XML_NO_ATTRIBUTE if the attribute - doesn't exist. If successful, the result of the conversion - will be written to 'value'. If not successful, nothing will - be written to 'value'. This allows you to provide default - value: - - @verbatim - int value = 10; - QueryIntAttribute( "foo", &value ); // if "foo" isn't found, value will still be 10 - @endverbatim - */ - XMLError QueryIntAttribute( const char* name, int* value ) const { - const XMLAttribute* a = FindAttribute( name ); - if ( !a ) { - return XML_NO_ATTRIBUTE; - } - return a->QueryIntValue( value ); - } - - /// See QueryIntAttribute() - XMLError QueryUnsignedAttribute( const char* name, unsigned int* value ) const { - const XMLAttribute* a = FindAttribute( name ); - if ( !a ) { - return XML_NO_ATTRIBUTE; - } - return a->QueryUnsignedValue( value ); - } - - /// See QueryIntAttribute() - XMLError QueryInt64Attribute(const char* name, int64_t* value) const { - const XMLAttribute* a = FindAttribute(name); - if (!a) { - return XML_NO_ATTRIBUTE; - } - return a->QueryInt64Value(value); - } - - /// See QueryIntAttribute() - XMLError QueryUnsigned64Attribute(const char* name, uint64_t* value) const { - const XMLAttribute* a = FindAttribute(name); - if(!a) { - return XML_NO_ATTRIBUTE; - } - return a->QueryUnsigned64Value(value); - } - - /// See QueryIntAttribute() - XMLError QueryBoolAttribute( const char* name, bool* value ) const { - const XMLAttribute* a = FindAttribute( name ); - if ( !a ) { - return XML_NO_ATTRIBUTE; - } - return a->QueryBoolValue( value ); - } - /// See QueryIntAttribute() - XMLError QueryDoubleAttribute( const char* name, double* value ) const { - const XMLAttribute* a = FindAttribute( name ); - if ( !a ) { - return XML_NO_ATTRIBUTE; - } - return a->QueryDoubleValue( value ); - } - /// See QueryIntAttribute() - XMLError QueryFloatAttribute( const char* name, float* value ) const { - const XMLAttribute* a = FindAttribute( name ); - if ( !a ) { - return XML_NO_ATTRIBUTE; - } - return a->QueryFloatValue( value ); - } - - /// See QueryIntAttribute() - XMLError QueryStringAttribute(const char* name, const char** value) const { - const XMLAttribute* a = FindAttribute(name); - if (!a) { - return XML_NO_ATTRIBUTE; - } - *value = a->Value(); - return XML_SUCCESS; - } - - - - /** Given an attribute name, QueryAttribute() returns - XML_SUCCESS, XML_WRONG_ATTRIBUTE_TYPE if the conversion - can't be performed, or XML_NO_ATTRIBUTE if the attribute - doesn't exist. It is overloaded for the primitive types, - and is a generally more convenient replacement of - QueryIntAttribute() and related functions. - - If successful, the result of the conversion - will be written to 'value'. If not successful, nothing will - be written to 'value'. This allows you to provide default - value: - - @verbatim - int value = 10; - QueryAttribute( "foo", &value ); // if "foo" isn't found, value will still be 10 - @endverbatim - */ - XMLError QueryAttribute( const char* name, int* value ) const { - return QueryIntAttribute( name, value ); - } - - XMLError QueryAttribute( const char* name, unsigned int* value ) const { - return QueryUnsignedAttribute( name, value ); - } - - XMLError QueryAttribute(const char* name, int64_t* value) const { - return QueryInt64Attribute(name, value); - } - - XMLError QueryAttribute(const char* name, uint64_t* value) const { - return QueryUnsigned64Attribute(name, value); - } - - XMLError QueryAttribute( const char* name, bool* value ) const { - return QueryBoolAttribute( name, value ); - } - - XMLError QueryAttribute( const char* name, double* value ) const { - return QueryDoubleAttribute( name, value ); - } - - XMLError QueryAttribute( const char* name, float* value ) const { - return QueryFloatAttribute( name, value ); - } - - XMLError QueryAttribute(const char* name, const char** value) const { - return QueryStringAttribute(name, value); - } - - /// Sets the named attribute to value. - void SetAttribute( const char* name, const char* value ) { - XMLAttribute* a = FindOrCreateAttribute( name ); - a->SetAttribute( value ); - } - /// Sets the named attribute to value. - void SetAttribute( const char* name, int value ) { - XMLAttribute* a = FindOrCreateAttribute( name ); - a->SetAttribute( value ); - } - /// Sets the named attribute to value. - void SetAttribute( const char* name, unsigned value ) { - XMLAttribute* a = FindOrCreateAttribute( name ); - a->SetAttribute( value ); - } - - /// Sets the named attribute to value. - void SetAttribute(const char* name, int64_t value) { - XMLAttribute* a = FindOrCreateAttribute(name); - a->SetAttribute(value); - } - - /// Sets the named attribute to value. - void SetAttribute(const char* name, uint64_t value) { - XMLAttribute* a = FindOrCreateAttribute(name); - a->SetAttribute(value); - } - - /// Sets the named attribute to value. - void SetAttribute( const char* name, bool value ) { - XMLAttribute* a = FindOrCreateAttribute( name ); - a->SetAttribute( value ); - } - /// Sets the named attribute to value. - void SetAttribute( const char* name, double value ) { - XMLAttribute* a = FindOrCreateAttribute( name ); - a->SetAttribute( value ); - } - /// Sets the named attribute to value. - void SetAttribute( const char* name, float value ) { - XMLAttribute* a = FindOrCreateAttribute( name ); - a->SetAttribute( value ); - } - - /** - Delete an attribute. - */ - void DeleteAttribute( const char* name ); - - /// Return the first attribute in the list. - const XMLAttribute* FirstAttribute() const { - return _rootAttribute; - } - /// Query a specific attribute in the list. - const XMLAttribute* FindAttribute( const char* name ) const; - - /** Convenience function for easy access to the text inside an element. Although easy - and concise, GetText() is limited compared to getting the XMLText child - and accessing it directly. - - If the first child of 'this' is a XMLText, the GetText() - returns the character string of the Text node, else null is returned. - - This is a convenient method for getting the text of simple contained text: - @verbatim - This is text - const char* str = fooElement->GetText(); - @endverbatim - - 'str' will be a pointer to "This is text". - - Note that this function can be misleading. If the element foo was created from - this XML: - @verbatim - This is text - @endverbatim - - then the value of str would be null. The first child node isn't a text node, it is - another element. From this XML: - @verbatim - This is text - @endverbatim - GetText() will return "This is ". - */ - const char* GetText() const; - - /** Convenience function for easy access to the text inside an element. Although easy - and concise, SetText() is limited compared to creating an XMLText child - and mutating it directly. - - If the first child of 'this' is a XMLText, SetText() sets its value to - the given string, otherwise it will create a first child that is an XMLText. - - This is a convenient method for setting the text of simple contained text: - @verbatim - This is text - fooElement->SetText( "Hullaballoo!" ); - Hullaballoo! - @endverbatim - - Note that this function can be misleading. If the element foo was created from - this XML: - @verbatim - This is text - @endverbatim - - then it will not change "This is text", but rather prefix it with a text element: - @verbatim - Hullaballoo!This is text - @endverbatim - - For this XML: - @verbatim - - @endverbatim - SetText() will generate - @verbatim - Hullaballoo! - @endverbatim - */ - void SetText( const char* inText ); - /// Convenience method for setting text inside an element. See SetText() for important limitations. - void SetText( int value ); - /// Convenience method for setting text inside an element. See SetText() for important limitations. - void SetText( unsigned value ); - /// Convenience method for setting text inside an element. See SetText() for important limitations. - void SetText(int64_t value); - /// Convenience method for setting text inside an element. See SetText() for important limitations. - void SetText(uint64_t value); - /// Convenience method for setting text inside an element. See SetText() for important limitations. - void SetText( bool value ); - /// Convenience method for setting text inside an element. See SetText() for important limitations. - void SetText( double value ); - /// Convenience method for setting text inside an element. See SetText() for important limitations. - void SetText( float value ); - - /** - Convenience method to query the value of a child text node. This is probably best - shown by example. Given you have a document is this form: - @verbatim - - 1 - 1.4 - - @endverbatim - - The QueryIntText() and similar functions provide a safe and easier way to get to the - "value" of x and y. - - @verbatim - int x = 0; - float y = 0; // types of x and y are contrived for example - const XMLElement* xElement = pointElement->FirstChildElement( "x" ); - const XMLElement* yElement = pointElement->FirstChildElement( "y" ); - xElement->QueryIntText( &x ); - yElement->QueryFloatText( &y ); - @endverbatim - - @returns XML_SUCCESS (0) on success, XML_CAN_NOT_CONVERT_TEXT if the text cannot be converted - to the requested type, and XML_NO_TEXT_NODE if there is no child text to query. - - */ - XMLError QueryIntText( int* ival ) const; - /// See QueryIntText() - XMLError QueryUnsignedText( unsigned* uval ) const; - /// See QueryIntText() - XMLError QueryInt64Text(int64_t* uval) const; - /// See QueryIntText() - XMLError QueryUnsigned64Text(uint64_t* uval) const; - /// See QueryIntText() - XMLError QueryBoolText( bool* bval ) const; - /// See QueryIntText() - XMLError QueryDoubleText( double* dval ) const; - /// See QueryIntText() - XMLError QueryFloatText( float* fval ) const; - - int IntText(int defaultValue = 0) const; - - /// See QueryIntText() - unsigned UnsignedText(unsigned defaultValue = 0) const; - /// See QueryIntText() - int64_t Int64Text(int64_t defaultValue = 0) const; - /// See QueryIntText() - uint64_t Unsigned64Text(uint64_t defaultValue = 0) const; - /// See QueryIntText() - bool BoolText(bool defaultValue = false) const; - /// See QueryIntText() - double DoubleText(double defaultValue = 0) const; - /// See QueryIntText() - float FloatText(float defaultValue = 0) const; - - /** - Convenience method to create a new XMLElement and add it as last (right) - child of this node. Returns the created and inserted element. - */ - XMLElement* InsertNewChildElement(const char* name); - /// See InsertNewChildElement() - XMLComment* InsertNewComment(const char* comment); - /// See InsertNewChildElement() - XMLText* InsertNewText(const char* text); - /// See InsertNewChildElement() - XMLDeclaration* InsertNewDeclaration(const char* text); - /// See InsertNewChildElement() - XMLUnknown* InsertNewUnknown(const char* text); - - - // internal: - enum ElementClosingType { - OPEN, // - CLOSED, // - CLOSING // - }; - ElementClosingType ClosingType() const { - return _closingType; - } - virtual XMLNode* ShallowClone( XMLDocument* document ) const override; - virtual bool ShallowEqual( const XMLNode* compare ) const override; - -protected: - char* ParseDeep( char* p, StrPair* parentEndTag, int* curLineNumPtr ) override; - -private: - XMLElement( XMLDocument* doc ); - virtual ~XMLElement(); - XMLElement( const XMLElement& ); // not supported - void operator=( const XMLElement& ); // not supported - - XMLAttribute* FindOrCreateAttribute( const char* name ); - char* ParseAttributes( char* p, int* curLineNumPtr ); - static void DeleteAttribute( XMLAttribute* attribute ); - XMLAttribute* CreateAttribute(); - - enum { BUF_SIZE = 200 }; - ElementClosingType _closingType; - // The attribute list is ordered; there is no 'lastAttribute' - // because the list needs to be scanned for dupes before adding - // a new attribute. - XMLAttribute* _rootAttribute; -}; - - -enum Whitespace { - PRESERVE_WHITESPACE, - COLLAPSE_WHITESPACE, - PEDANTIC_WHITESPACE -}; - - -/** A Document binds together all the functionality. - It can be saved, loaded, and printed to the screen. - All Nodes are connected and allocated to a Document. - If the Document is deleted, all its Nodes are also deleted. -*/ -class TINYXML2_LIB XMLDocument : public XMLNode -{ - friend class XMLElement; - // Gives access to SetError and Push/PopDepth, but over-access for everything else. - // Wishing C++ had "internal" scope. - friend class XMLNode; - friend class XMLText; - friend class XMLComment; - friend class XMLDeclaration; - friend class XMLUnknown; -public: - /// constructor - XMLDocument( bool processEntities = true, Whitespace whitespaceMode = PRESERVE_WHITESPACE ); - ~XMLDocument(); - - virtual XMLDocument* ToDocument() override { - TIXMLASSERT( this == _document ); - return this; - } - virtual const XMLDocument* ToDocument() const override { - TIXMLASSERT( this == _document ); - return this; - } - - /** - Parse an XML file from a character string. - Returns XML_SUCCESS (0) on success, or - an errorID. - - You may optionally pass in the 'nBytes', which is - the number of bytes which will be parsed. If not - specified, TinyXML-2 will assume 'xml' points to a - null terminated string. - */ - XMLError Parse( const char* xml, size_t nBytes=static_cast(-1) ); - - /** - Load an XML file from disk. - Returns XML_SUCCESS (0) on success, or - an errorID. - */ - XMLError LoadFile( const char* filename ); - - /** - Load an XML file from disk. You are responsible - for providing and closing the FILE*. - - NOTE: The file should be opened as binary ("rb") - not text in order for TinyXML-2 to correctly - do newline normalization. - - Returns XML_SUCCESS (0) on success, or - an errorID. - */ - XMLError LoadFile( FILE* ); - - /** - Save the XML file to disk. - Returns XML_SUCCESS (0) on success, or - an errorID. - */ - XMLError SaveFile( const char* filename, bool compact = false ); - - /** - Save the XML file to disk. You are responsible - for providing and closing the FILE*. - - Returns XML_SUCCESS (0) on success, or - an errorID. - */ - XMLError SaveFile( FILE* fp, bool compact = false ); - - bool ProcessEntities() const { - return _processEntities; - } - Whitespace WhitespaceMode() const { - return _whitespaceMode; - } - - /** - Returns true if this document has a leading Byte Order Mark of UTF8. - */ - bool HasBOM() const { - return _writeBOM; - } - /** Sets whether to write the BOM when writing the file. - */ - void SetBOM( bool useBOM ) { - _writeBOM = useBOM; - } - - /** Return the root element of DOM. Equivalent to FirstChildElement(). - To get the first node, use FirstChild(). - */ - XMLElement* RootElement() { - return FirstChildElement(); - } - const XMLElement* RootElement() const { - return FirstChildElement(); - } - - /** Print the Document. If the Printer is not provided, it will - print to stdout. If you provide Printer, this can print to a file: - @verbatim - XMLPrinter printer( fp ); - doc.Print( &printer ); - @endverbatim - - Or you can use a printer to print to memory: - @verbatim - XMLPrinter printer; - doc.Print( &printer ); - // printer.CStr() has a const char* to the XML - @endverbatim - */ - void Print( XMLPrinter* streamer=0 ) const; - virtual bool Accept( XMLVisitor* visitor ) const override; - - /** - Create a new Element associated with - this Document. The memory for the Element - is managed by the Document. - */ - XMLElement* NewElement( const char* name ); - /** - Create a new Comment associated with - this Document. The memory for the Comment - is managed by the Document. - */ - XMLComment* NewComment( const char* comment ); - /** - Create a new Text associated with - this Document. The memory for the Text - is managed by the Document. - */ - XMLText* NewText( const char* text ); - /** - Create a new Declaration associated with - this Document. The memory for the object - is managed by the Document. - - If the 'text' param is null, the standard - declaration is used.: - @verbatim - - @endverbatim - */ - XMLDeclaration* NewDeclaration( const char* text=0 ); - /** - Create a new Unknown associated with - this Document. The memory for the object - is managed by the Document. - */ - XMLUnknown* NewUnknown( const char* text ); - - /** - Delete a node associated with this document. - It will be unlinked from the DOM. - */ - void DeleteNode( XMLNode* node ); - - /// Clears the error flags. - void ClearError(); - - /// Return true if there was an error parsing the document. - bool Error() const { - return _errorID != XML_SUCCESS; - } - /// Return the errorID. - XMLError ErrorID() const { - return _errorID; - } - const char* ErrorName() const; - static const char* ErrorIDToName(XMLError errorID); - - /** Returns a "long form" error description. A hopefully helpful - diagnostic with location, line number, and/or additional info. - */ - const char* ErrorStr() const; - - /// A (trivial) utility function that prints the ErrorStr() to stdout. - void PrintError() const; - - /// Return the line where the error occurred, or zero if unknown. - int ErrorLineNum() const - { - return _errorLineNum; - } - - /// Clear the document, resetting it to the initial state. - void Clear(); - - /** - Copies this document to a target document. - The target will be completely cleared before the copy. - If you want to copy a sub-tree, see XMLNode::DeepClone(). - - NOTE: that the 'target' must be non-null. - */ - void DeepCopy(XMLDocument* target) const; - - // internal - char* Identify( char* p, XMLNode** node, bool first ); - - // internal - void MarkInUse(const XMLNode* const); - - virtual XMLNode* ShallowClone( XMLDocument* /*document*/ ) const override{ - return 0; - } - virtual bool ShallowEqual( const XMLNode* /*compare*/ ) const override{ - return false; - } - -private: - XMLDocument( const XMLDocument& ); // not supported - void operator=( const XMLDocument& ); // not supported - - bool _writeBOM; - bool _processEntities; - XMLError _errorID; - Whitespace _whitespaceMode; - mutable StrPair _errorStr; - int _errorLineNum; - char* _charBuffer; - int _parseCurLineNum; - int _parsingDepth; - // Memory tracking does add some overhead. - // However, the code assumes that you don't - // have a bunch of unlinked nodes around. - // Therefore it takes less memory to track - // in the document vs. a linked list in the XMLNode, - // and the performance is the same. - DynArray _unlinked; - - MemPoolT< sizeof(XMLElement) > _elementPool; - MemPoolT< sizeof(XMLAttribute) > _attributePool; - MemPoolT< sizeof(XMLText) > _textPool; - MemPoolT< sizeof(XMLComment) > _commentPool; - - static const char* _errorNames[XML_ERROR_COUNT]; - - void Parse(); - - void SetError( XMLError error, int lineNum, const char* format, ... ); - - // Something of an obvious security hole, once it was discovered. - // Either an ill-formed XML or an excessively deep one can overflow - // the stack. Track stack depth, and error out if needed. - class DepthTracker { - public: - explicit DepthTracker(XMLDocument * document) { - this->_document = document; - document->PushDepth(); - } - ~DepthTracker() { - _document->PopDepth(); - } - private: - XMLDocument * _document; - }; - void PushDepth(); - void PopDepth(); - - template - NodeType* CreateUnlinkedNode( MemPoolT& pool ); -}; - -template -inline NodeType* XMLDocument::CreateUnlinkedNode( MemPoolT& pool ) -{ - TIXMLASSERT( sizeof( NodeType ) == PoolElementSize ); - TIXMLASSERT( sizeof( NodeType ) == pool.ItemSize() ); - NodeType* returnNode = new (pool.Alloc()) NodeType( this ); - TIXMLASSERT( returnNode ); - returnNode->_memPool = &pool; - - _unlinked.Push(returnNode); - return returnNode; -} - -/** - A XMLHandle is a class that wraps a node pointer with null checks; this is - an incredibly useful thing. Note that XMLHandle is not part of the TinyXML-2 - DOM structure. It is a separate utility class. - - Take an example: - @verbatim - - - - - - - @endverbatim - - Assuming you want the value of "attributeB" in the 2nd "Child" element, it's very - easy to write a *lot* of code that looks like: - - @verbatim - XMLElement* root = document.FirstChildElement( "Document" ); - if ( root ) - { - XMLElement* element = root->FirstChildElement( "Element" ); - if ( element ) - { - XMLElement* child = element->FirstChildElement( "Child" ); - if ( child ) - { - XMLElement* child2 = child->NextSiblingElement( "Child" ); - if ( child2 ) - { - // Finally do something useful. - @endverbatim - - And that doesn't even cover "else" cases. XMLHandle addresses the verbosity - of such code. A XMLHandle checks for null pointers so it is perfectly safe - and correct to use: - - @verbatim - XMLHandle docHandle( &document ); - XMLElement* child2 = docHandle.FirstChildElement( "Document" ).FirstChildElement( "Element" ).FirstChildElement().NextSiblingElement(); - if ( child2 ) - { - // do something useful - @endverbatim - - Which is MUCH more concise and useful. - - It is also safe to copy handles - internally they are nothing more than node pointers. - @verbatim - XMLHandle handleCopy = handle; - @endverbatim - - See also XMLConstHandle, which is the same as XMLHandle, but operates on const objects. -*/ -class TINYXML2_LIB XMLHandle -{ -public: - /// Create a handle from any node (at any depth of the tree.) This can be a null pointer. - explicit XMLHandle( XMLNode* node ) : _node( node ) { - } - /// Create a handle from a node. - explicit XMLHandle( XMLNode& node ) : _node( &node ) { - } - /// Copy constructor - XMLHandle( const XMLHandle& ref ) : _node( ref._node ) { - } - /// Assignment - XMLHandle& operator=( const XMLHandle& ref ) { - _node = ref._node; - return *this; - } - - /// Get the first child of this handle. - XMLHandle FirstChild() { - return XMLHandle( _node ? _node->FirstChild() : 0 ); - } - /// Get the first child element of this handle. - XMLHandle FirstChildElement( const char* name = 0 ) { - return XMLHandle( _node ? _node->FirstChildElement( name ) : 0 ); - } - /// Get the last child of this handle. - XMLHandle LastChild() { - return XMLHandle( _node ? _node->LastChild() : 0 ); - } - /// Get the last child element of this handle. - XMLHandle LastChildElement( const char* name = 0 ) { - return XMLHandle( _node ? _node->LastChildElement( name ) : 0 ); - } - /// Get the previous sibling of this handle. - XMLHandle PreviousSibling() { - return XMLHandle( _node ? _node->PreviousSibling() : 0 ); - } - /// Get the previous sibling element of this handle. - XMLHandle PreviousSiblingElement( const char* name = 0 ) { - return XMLHandle( _node ? _node->PreviousSiblingElement( name ) : 0 ); - } - /// Get the next sibling of this handle. - XMLHandle NextSibling() { - return XMLHandle( _node ? _node->NextSibling() : 0 ); - } - /// Get the next sibling element of this handle. - XMLHandle NextSiblingElement( const char* name = 0 ) { - return XMLHandle( _node ? _node->NextSiblingElement( name ) : 0 ); - } - - /// Safe cast to XMLNode. This can return null. - XMLNode* ToNode() { - return _node; - } - /// Safe cast to XMLElement. This can return null. - XMLElement* ToElement() { - return ( _node ? _node->ToElement() : 0 ); - } - /// Safe cast to XMLText. This can return null. - XMLText* ToText() { - return ( _node ? _node->ToText() : 0 ); - } - /// Safe cast to XMLUnknown. This can return null. - XMLUnknown* ToUnknown() { - return ( _node ? _node->ToUnknown() : 0 ); - } - /// Safe cast to XMLDeclaration. This can return null. - XMLDeclaration* ToDeclaration() { - return ( _node ? _node->ToDeclaration() : 0 ); - } - -private: - XMLNode* _node; -}; - - -/** - A variant of the XMLHandle class for working with const XMLNodes and Documents. It is the - same in all regards, except for the 'const' qualifiers. See XMLHandle for API. -*/ -class TINYXML2_LIB XMLConstHandle -{ -public: - explicit XMLConstHandle( const XMLNode* node ) : _node( node ) { - } - explicit XMLConstHandle( const XMLNode& node ) : _node( &node ) { - } - XMLConstHandle( const XMLConstHandle& ref ) : _node( ref._node ) { - } - - XMLConstHandle& operator=( const XMLConstHandle& ref ) { - _node = ref._node; - return *this; - } - - const XMLConstHandle FirstChild() const { - return XMLConstHandle( _node ? _node->FirstChild() : 0 ); - } - const XMLConstHandle FirstChildElement( const char* name = 0 ) const { - return XMLConstHandle( _node ? _node->FirstChildElement( name ) : 0 ); - } - const XMLConstHandle LastChild() const { - return XMLConstHandle( _node ? _node->LastChild() : 0 ); - } - const XMLConstHandle LastChildElement( const char* name = 0 ) const { - return XMLConstHandle( _node ? _node->LastChildElement( name ) : 0 ); - } - const XMLConstHandle PreviousSibling() const { - return XMLConstHandle( _node ? _node->PreviousSibling() : 0 ); - } - const XMLConstHandle PreviousSiblingElement( const char* name = 0 ) const { - return XMLConstHandle( _node ? _node->PreviousSiblingElement( name ) : 0 ); - } - const XMLConstHandle NextSibling() const { - return XMLConstHandle( _node ? _node->NextSibling() : 0 ); - } - const XMLConstHandle NextSiblingElement( const char* name = 0 ) const { - return XMLConstHandle( _node ? _node->NextSiblingElement( name ) : 0 ); - } - - - const XMLNode* ToNode() const { - return _node; - } - const XMLElement* ToElement() const { - return ( _node ? _node->ToElement() : 0 ); - } - const XMLText* ToText() const { - return ( _node ? _node->ToText() : 0 ); - } - const XMLUnknown* ToUnknown() const { - return ( _node ? _node->ToUnknown() : 0 ); - } - const XMLDeclaration* ToDeclaration() const { - return ( _node ? _node->ToDeclaration() : 0 ); - } - -private: - const XMLNode* _node; -}; - - -/** - Printing functionality. The XMLPrinter gives you more - options than the XMLDocument::Print() method. - - It can: - -# Print to memory. - -# Print to a file you provide. - -# Print XML without a XMLDocument. - - Print to Memory - - @verbatim - XMLPrinter printer; - doc.Print( &printer ); - SomeFunction( printer.CStr() ); - @endverbatim - - Print to a File - - You provide the file pointer. - @verbatim - XMLPrinter printer( fp ); - doc.Print( &printer ); - @endverbatim - - Print without a XMLDocument - - When loading, an XML parser is very useful. However, sometimes - when saving, it just gets in the way. The code is often set up - for streaming, and constructing the DOM is just overhead. - - The Printer supports the streaming case. The following code - prints out a trivially simple XML file without ever creating - an XML document. - - @verbatim - XMLPrinter printer( fp ); - printer.OpenElement( "foo" ); - printer.PushAttribute( "foo", "bar" ); - printer.CloseElement(); - @endverbatim -*/ -class TINYXML2_LIB XMLPrinter : public XMLVisitor -{ -public: - /** Construct the printer. If the FILE* is specified, - this will print to the FILE. Else it will print - to memory, and the result is available in CStr(). - If 'compact' is set to true, then output is created - with only required whitespace and newlines. - */ - XMLPrinter( FILE* file=0, bool compact = false, int depth = 0 ); - virtual ~XMLPrinter() {} - - /** If streaming, write the BOM and declaration. */ - void PushHeader( bool writeBOM, bool writeDeclaration ); - /** If streaming, start writing an element. - The element must be closed with CloseElement() - */ - void OpenElement( const char* name, bool compactMode=false ); - /// If streaming, add an attribute to an open element. - void PushAttribute( const char* name, const char* value ); - void PushAttribute( const char* name, int value ); - void PushAttribute( const char* name, unsigned value ); - void PushAttribute( const char* name, int64_t value ); - void PushAttribute( const char* name, uint64_t value ); - void PushAttribute( const char* name, bool value ); - void PushAttribute( const char* name, double value ); - /// If streaming, close the Element. - virtual void CloseElement( bool compactMode=false ); - - /// Add a text node. - void PushText( const char* text, bool cdata=false ); - /// Add a text node from an integer. - void PushText( int value ); - /// Add a text node from an unsigned. - void PushText( unsigned value ); - /// Add a text node from a signed 64bit integer. - void PushText( int64_t value ); - /// Add a text node from an unsigned 64bit integer. - void PushText( uint64_t value ); - /// Add a text node from a bool. - void PushText( bool value ); - /// Add a text node from a float. - void PushText( float value ); - /// Add a text node from a double. - void PushText( double value ); - - /// Add a comment - void PushComment( const char* comment ); - - void PushDeclaration( const char* value ); - void PushUnknown( const char* value ); - - virtual bool VisitEnter( const XMLDocument& /*doc*/ ) override; - virtual bool VisitExit( const XMLDocument& /*doc*/ ) override { - return true; - } - - virtual bool VisitEnter( const XMLElement& element, const XMLAttribute* attribute ) override; - virtual bool VisitExit( const XMLElement& element ) override; - - virtual bool Visit( const XMLText& text ) override; - virtual bool Visit( const XMLComment& comment ) override; - virtual bool Visit( const XMLDeclaration& declaration ) override; - virtual bool Visit( const XMLUnknown& unknown ) override; - - /** - If in print to memory mode, return a pointer to - the XML file in memory. - */ - const char* CStr() const { - return _buffer.Mem(); - } - /** - If in print to memory mode, return the size - of the XML file in memory. (Note the size returned - includes the terminating null.) - */ - size_t CStrSize() const { - return _buffer.Size(); - } - /** - If in print to memory mode, reset the buffer to the - beginning. - */ - void ClearBuffer( bool resetToFirstElement = true ) { - _buffer.Clear(); - _buffer.Push(0); - _firstElement = resetToFirstElement; - } - -protected: - virtual bool CompactMode( const XMLElement& ) { return _compactMode; } - - /** Prints out the space before an element. You may override to change - the space and tabs used. A PrintSpace() override should call Print(). - */ - virtual void PrintSpace( int depth ); - virtual void Print( const char* format, ... ); - virtual void Write( const char* data, size_t size ); - virtual void Putc( char ch ); - - inline void Write(const char* data) { Write(data, strlen(data)); } - - void SealElementIfJustOpened(); - bool _elementJustOpened; - DynArray< const char*, 10 > _stack; - -private: - /** - Prepares to write a new node. This includes sealing an element that was - just opened, and writing any whitespace necessary if not in compact mode. - */ - void PrepareForNewNode( bool compactMode ); - void PrintString( const char*, bool restrictedEntitySet ); // prints out, after detecting entities. - - bool _firstElement; - FILE* _fp; - int _depth; - int _textDepth; - bool _processEntities; - bool _compactMode; - - enum { - ENTITY_RANGE = 64, - BUF_SIZE = 200 - }; - bool _entityFlag[ENTITY_RANGE]; - bool _restrictedEntityFlag[ENTITY_RANGE]; - - DynArray< char, 20 > _buffer; - - // Prohibit cloning, intentionally not implemented - XMLPrinter( const XMLPrinter& ); - XMLPrinter& operator=( const XMLPrinter& ); -}; - - -} // namespace tinyxml2 - -#if defined(_MSC_VER) -# pragma warning(pop) -#endif - -#endif // TINYXML2_INCLUDED diff --git a/src/xmutil/third_party/xmutil/ContextInfo.cpp b/src/xmutil/third_party/xmutil/ContextInfo.cpp deleted file mode 100644 index 0c9f710b9..000000000 --- a/src/xmutil/third_party/xmutil/ContextInfo.cpp +++ /dev/null @@ -1,60 +0,0 @@ -#include "ContextInfo.h" - -#include "Symbol/Expression.h" -#include "Symbol/Symbol.h" -#include "Symbol/Variable.h" - -Symbol *ContextInfo::GetLHSSpecific(Symbol *dim) { - if (!pLHSElmsGeneric || dim->isType() != Symtype_Variable) - return dim; - size_t n = pLHSElmsGeneric->size(); - for (size_t i = 0; i < n; i++) { - if ((*pLHSElmsGeneric)[i] == dim) - return (*pLHSElmsSpecific)[i]; - } - // no match - // see if dim has anything it maps to - if so look for that on the LHS - then take the corresponding specific element - Variable *v = static_cast(dim); - std::vector eqs = v->GetAllEquations(); - if (!eqs.empty()) { - Expression *exp = eqs[0]->GetExpression(); - assert(exp->GetType() == EXPTYPE_Symlist); - if (exp->GetType() == EXPTYPE_Symlist) { - ExpressionSymbolList *esl = static_cast(exp); - SymbolList *map = esl->Map(); - if (map) { - // I believe we are missing the case where the mapping is explicitly laid out in a list, but I don't remember - // the syntax for this walk through the things mapped to - size_t count = map->Length(); - for (size_t j = 0; j < count; j++) { - const SymbolList::SymbolListEntry &sle = (*map)[j]; - if (sle.eType == SymbolList::EntryType_SYMBOL) // only type know how to deal with - { - Symbol *owner = sle.u.pSymbol; // shoulw be asubscript range - for (size_t i = 0; i < n; i++) { - if ((*pLHSElmsGeneric)[i] == owner) { - // look for pLHSElmsSpecific in the entries for owner as subscript definition - use that position from - // v above - // Variable* mv = static_cast(owner); - - std::vector list; - Equation::GetSubscriptElements(list, owner); - for (size_t k = 0; k < list.size(); k++) { - if (list[k] == (*pLHSElmsSpecific)[i]) { - std::vector ours; - Equation::GetSubscriptElements(ours, v); - if (ours.size() == list.size()) - return ours[k]; - } - } - } - } - break; - } - } - } - } - } - - return dim; -} diff --git a/src/xmutil/third_party/xmutil/ContextInfo.h b/src/xmutil/third_party/xmutil/ContextInfo.h deleted file mode 100644 index 7b7a88b50..000000000 --- a/src/xmutil/third_party/xmutil/ContextInfo.h +++ /dev/null @@ -1,146 +0,0 @@ -#ifndef _XMUTIL_CONTEXTINFO_H -#define _XMUTIL_CONTEXTINFO_H -#include -#include -#include -/* a utility class helpfu in sorting and evaluating equations - */ - -/* computing flags - note that the incomp version must be the - flag << 1 */ -#define CF_xmile_output 0 -#define CF_active 1 // int testing most the work is done here -#define CF_active_incomp 2 -#define CF_unchanging 4 // subset of active not changing over time -#define CF_unchanging_incomp 8 -#define CF_rate 16 // another subset of active -#define CF_rate_incomp 32 -#define CF_initial 64 -#define CF_initial_incomp 128 // that is it for a unsigned char - -/* dynamic dependency flags - allows optimization of what is computed and what skipped */ -#define DDF_constant 1 -#define DDF_initial 2 -#define DDF_time_varying 4 -#define DDF_data 8 -#define DDF_level 16 - -class Model; -class SymbolNameSpace; -class Equation; // forward -class Symbol; -class Variable; - -class ContextInfo : public std::ostringstream { -public: - ContextInfo(Variable *lhs) { - pLHS = lhs; - iComputeType = 0; - bInitEqn = false; - bInSubList = false; - bSelfIsPrevious = false; - pEquations = NULL; - bWantFinalStar = false; - } - ~ContextInfo(void) { - } - friend class Model; - // ContextInfo& operator << (const char *s) { std::cout << s ; return *this; } - // ContextInfo& operator << (const std::string& s) {std::cout << s.c_str(); return *this; } - // ContextInfo& operator << (const double num) { std::cout << num ; return *this ;} - inline int GetComputeType(void) { - return iComputeType; - } - inline void SetComputType(int type) { - iComputeType = type; - } - inline bool InitEqn() { - return bInitEqn; - } - inline void SetInitEqn(bool set) { - bInitEqn = set; - } - inline double *GetLevelP(int count) { - double *r = pCurLevel; - pCurLevel += count; - return r; - } - inline double *GetRateP(int count) { - double *r = pCurRate; - pCurRate += count; - return r; - } - inline double *GetAuxP(int count) { - double *r = pCurAux; - pCurAux += count; - return r; - } - inline void PushEquation(Equation *e) { - if (pEquations) - pEquations->push_back(e); - } - inline SymbolNameSpace *GetSymbolNameSpace(void) { - return pSymbolNameSpace; - } - inline unsigned char GetDDF(void) { - return cDynamicDependencyFlag; - } - inline void ClearDDF(void) { - cDynamicDependencyFlag = 0; - } - inline void AddDDF(unsigned char flag) { - cDynamicDependencyFlag |= flag; - } - inline double GetTime(void) { - return dTime; - } - inline double GetDT(void) { - return dDT; - } - inline void SetLHSElms(const std::vector *generic, const std::vector *specific) { - assert(specific->size() == generic->size()); - pLHSElmsGeneric = generic; - pLHSElmsSpecific = specific; - } - Symbol *GetLHSSpecific(Symbol *generic); - bool InSubList() const { - return bInSubList; - } - void SetInSubList(bool set) { - bInSubList = set; - } - bool SelfIsPrevious() const { - return bSelfIsPrevious; - } - void SetSelfIsPrevious(bool set) { - bSelfIsPrevious = set; - } - bool WantFinalStar() const { - return bWantFinalStar; - } - void SetWantFinalStar(bool set) { - bWantFinalStar = set; - } - Variable *LHS() { - return pLHS; - } - -private: - double dTime, dDT; - double *pBaseLevel, *pCurLevel; - double *pBaseRate, *pCurRate; - double *pBaseAux, *pCurAux; - SymbolNameSpace *pSymbolNameSpace; - const std::vector *pLHSElmsGeneric; // left hand side current settings of subscripts - const std::vector *pLHSElmsSpecific; // left hand side current settings of subscripts - std::vector *pEquations; /* passed from model - active or initial or... */ - Variable *pLHS; - int iComputeType; // CF_... as above - unsigned char cDynamicDependencyFlag; // DDF_... as above - bool bInitEqn; // for xmile - bool bInSubList; - bool bSelfIsPrevious; - bool bWantFinalStar; -}; - -#endif diff --git a/src/xmutil/third_party/xmutil/Dynamo/DYacc.tab.cpp b/src/xmutil/third_party/xmutil/Dynamo/DYacc.tab.cpp deleted file mode 100644 index ed9850284..000000000 --- a/src/xmutil/third_party/xmutil/Dynamo/DYacc.tab.cpp +++ /dev/null @@ -1,1815 +0,0 @@ -/* A Bison parser, made by GNU Bison 3.8.2. */ - -/* Bison implementation for Yacc-like parsers in C - - Copyright (C) 1984, 1989-1990, 2000-2015, 2018-2021 Free Software Foundation, - Inc. - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . */ - -/* As a special exception, you may create a larger work that contains - part or all of the Bison parser skeleton and distribute that work - under terms of your choice, so long as that work isn't itself a - parser generator using the skeleton or a modified version thereof - as a parser skeleton. Alternatively, if you modify or redistribute - the parser skeleton itself, you may (at your option) remove this - special exception, which will cause the skeleton and the resulting - Bison output files to be licensed under the GNU General Public - License without this special exception. - - This special exception was added by the Free Software Foundation in - version 2.2 of Bison. */ - -/* C LALR(1) parser skeleton written by Richard Stallman, by - simplifying the original so-called "semantic" parser. */ - -/* DO NOT RELY ON FEATURES THAT ARE NOT DOCUMENTED in the manual, - especially those whose name start with YY_ or yy_. They are - private implementation details that can be changed or removed. */ - -/* All symbols defined below should begin with yy or YY, to avoid - infringing on user name space. This should be done even for local - variables, as they might otherwise be expanded by user macros. - There are some unavoidable exceptions within include files to - define necessary library symbols; they are noted "INFRINGES ON - USER NAME SPACE" below. */ - -/* Identify Bison output, and Bison version. */ -#define YYBISON 30802 - -/* Bison version string. */ -#define YYBISON_VERSION "3.8.2" - -/* Skeleton name. */ -#define YYSKELETON_NAME "yacc.c" - -/* Pure parsers. */ -#define YYPURE 0 - -/* Push parsers. */ -#define YYPUSH 0 - -/* Pull parsers. */ -#define YYPULL 1 - -/* Substitute the variable and function names. */ -#define yyparse dpyyparse -#define yylex dpyylex -#define yyerror dpyyerror -#define yydebug dpyydebug -#define yynerrs dpyynerrs -#define yylval dpyylval -#define yychar dpyychar - -/* First part of user prologue. */ -#line 10 "DYacc.y" - -#include "../Log.h" -#include "../Symbol/Parse.h" -#include "DynamoParseFunctions.h" -extern int dpyylex(void); -extern void dpyyerror(char const *); -#define YYSTYPE ParseUnion -#define YYFPRINTF fprintf - -#line 88 "DYacc.tab.cpp" - -#ifndef YY_CAST -#ifdef __cplusplus -#define YY_CAST(Type, Val) static_cast(Val) -#define YY_REINTERPRET_CAST(Type, Val) reinterpret_cast(Val) -#else -#define YY_CAST(Type, Val) ((Type)(Val)) -#define YY_REINTERPRET_CAST(Type, Val) ((Type)(Val)) -#endif -#endif -#ifndef YY_NULLPTR -#if defined __cplusplus -#if 201103L <= __cplusplus -#define YY_NULLPTR nullptr -#else -#define YY_NULLPTR 0 -#endif -#else -#define YY_NULLPTR ((void *)0) -#endif -#endif - -#include "DYacc.tab.hpp" -/* Symbol kind. */ -enum yysymbol_kind_t { - YYSYMBOL_YYEMPTY = -2, - YYSYMBOL_YYEOF = 0, /* "end of file" */ - YYSYMBOL_YYerror = 1, /* error */ - YYSYMBOL_YYUNDEF = 2, /* "invalid token" */ - YYSYMBOL_DPTT_aux = 3, /* DPTT_aux */ - YYSYMBOL_DPTT_table = 4, /* DPTT_table */ - YYSYMBOL_DPTT_level = 5, /* DPTT_level */ - YYSYMBOL_DPTT_init = 6, /* DPTT_init */ - YYSYMBOL_DPTT_constant = 7, /* DPTT_constant */ - YYSYMBOL_DPTT_eoq = 8, /* DPTT_eoq */ - YYSYMBOL_DPTT_groupstar = 9, /* DPTT_groupstar */ - YYSYMBOL_DPTT_specs = 10, /* DPTT_specs */ - YYSYMBOL_DPTT_save = 11, /* DPTT_save */ - YYSYMBOL_DPTT_and = 12, /* DPTT_and */ - YYSYMBOL_DPTT_macro = 13, /* DPTT_macro */ - YYSYMBOL_DPTT_end_of_macro = 14, /* DPTT_end_of_macro */ - YYSYMBOL_DPTT_or = 15, /* DPTT_or */ - YYSYMBOL_DPTT_not = 16, /* DPTT_not */ - YYSYMBOL_DPTT_ge = 17, /* DPTT_ge */ - YYSYMBOL_DPTT_le = 18, /* DPTT_le */ - YYSYMBOL_DPTT_ne = 19, /* DPTT_ne */ - YYSYMBOL_DPTT_number = 20, /* DPTT_number */ - YYSYMBOL_DPTT_symbol = 21, /* DPTT_symbol */ - YYSYMBOL_DPTT_units_symbol = 22, /* DPTT_units_symbol */ - YYSYMBOL_DPTT_function = 23, /* DPTT_function */ - YYSYMBOL_DPTT_dt_to_one = 24, /* DPTT_dt_to_one */ - YYSYMBOL_25_ = 25, /* '-' */ - YYSYMBOL_26_ = 26, /* '+' */ - YYSYMBOL_27_ = 27, /* '=' */ - YYSYMBOL_28_ = 28, /* '<' */ - YYSYMBOL_29_ = 29, /* '>' */ - YYSYMBOL_30_ = 30, /* '*' */ - YYSYMBOL_31_ = 31, /* '/' */ - YYSYMBOL_32_ = 32, /* '^' */ - YYSYMBOL_33_ = 33, /* ',' */ - YYSYMBOL_34_ = 34, /* '(' */ - YYSYMBOL_35_ = 35, /* ')' */ - YYSYMBOL_36_ = 36, /* ':' */ - YYSYMBOL_37_ = 37, /* '[' */ - YYSYMBOL_38_ = 38, /* ']' */ - YYSYMBOL_39_ = 39, /* '!' */ - YYSYMBOL_40_ = 40, /* ';' */ - YYSYMBOL_YYACCEPT = 41, /* $accept */ - YYSYMBOL_fulleq = 42, /* fulleq */ - YYSYMBOL_teqn = 43, /* teqn */ - YYSYMBOL_tabledef = 44, /* tabledef */ - YYSYMBOL_eqn = 45, /* eqn */ - YYSYMBOL_stock_eqn = 46, /* stock_eqn */ - YYSYMBOL_lhs = 47, /* lhs */ - YYSYMBOL_var = 48, /* var */ - YYSYMBOL_sublist = 49, /* sublist */ - YYSYMBOL_symlist = 50, /* symlist */ - YYSYMBOL_subdef = 51, /* subdef */ - YYSYMBOL_number = 52, /* number */ - YYSYMBOL_maplist = 53, /* maplist */ - YYSYMBOL_exprlist = 54, /* exprlist */ - YYSYMBOL_exp = 55, /* exp */ - YYSYMBOL_tablevals = 56, /* tablevals */ - YYSYMBOL_xytablevals = 57, /* xytablevals */ - YYSYMBOL_xytablevec = 58, /* xytablevec */ - YYSYMBOL_tablepairs = 59 /* tablepairs */ -}; -typedef enum yysymbol_kind_t yysymbol_kind_t; - -#ifdef short -#undef short -#endif - -/* On compilers that do not define __PTRDIFF_MAX__ etc., make sure - and (if available) are included - so that the code can choose integer types of a good width. */ - -#ifndef __PTRDIFF_MAX__ -#include /* INFRINGES ON USER NAME SPACE */ -#if defined __STDC_VERSION__ && 199901 <= __STDC_VERSION__ -#include /* INFRINGES ON USER NAME SPACE */ -#define YY_STDINT_H -#endif -#endif - -/* Narrow types that promote to a signed type and that can represent a - signed or unsigned integer of at least N bits. In tables they can - save space and decrease cache pressure. Promoting to a signed type - helps avoid bugs in integer arithmetic. */ - -#ifdef __INT_LEAST8_MAX__ -typedef __INT_LEAST8_TYPE__ yytype_int8; -#elif defined YY_STDINT_H -typedef int_least8_t yytype_int8; -#else -typedef signed char yytype_int8; -#endif - -#ifdef __INT_LEAST16_MAX__ -typedef __INT_LEAST16_TYPE__ yytype_int16; -#elif defined YY_STDINT_H -typedef int_least16_t yytype_int16; -#else -typedef short yytype_int16; -#endif - -/* Work around bug in HP-UX 11.23, which defines these macros - incorrectly for preprocessor constants. This workaround can likely - be removed in 2023, as HPE has promised support for HP-UX 11.23 - (aka HP-UX 11i v2) only through the end of 2022; see Table 2 of - . */ -#ifdef __hpux -#undef UINT_LEAST8_MAX -#undef UINT_LEAST16_MAX -#define UINT_LEAST8_MAX 255 -#define UINT_LEAST16_MAX 65535 -#endif - -#if defined __UINT_LEAST8_MAX__ && __UINT_LEAST8_MAX__ <= __INT_MAX__ -typedef __UINT_LEAST8_TYPE__ yytype_uint8; -#elif (!defined __UINT_LEAST8_MAX__ && defined YY_STDINT_H && UINT_LEAST8_MAX <= INT_MAX) -typedef uint_least8_t yytype_uint8; -#elif !defined __UINT_LEAST8_MAX__ && UCHAR_MAX <= INT_MAX -typedef unsigned char yytype_uint8; -#else -typedef short yytype_uint8; -#endif - -#if defined __UINT_LEAST16_MAX__ && __UINT_LEAST16_MAX__ <= __INT_MAX__ -typedef __UINT_LEAST16_TYPE__ yytype_uint16; -#elif (!defined __UINT_LEAST16_MAX__ && defined YY_STDINT_H && UINT_LEAST16_MAX <= INT_MAX) -typedef uint_least16_t yytype_uint16; -#elif !defined __UINT_LEAST16_MAX__ && USHRT_MAX <= INT_MAX -typedef unsigned short yytype_uint16; -#else -typedef int yytype_uint16; -#endif - -#ifndef YYPTRDIFF_T -#if defined __PTRDIFF_TYPE__ && defined __PTRDIFF_MAX__ -#define YYPTRDIFF_T __PTRDIFF_TYPE__ -#define YYPTRDIFF_MAXIMUM __PTRDIFF_MAX__ -#elif defined PTRDIFF_MAX -#ifndef ptrdiff_t -#include /* INFRINGES ON USER NAME SPACE */ -#endif -#define YYPTRDIFF_T ptrdiff_t -#define YYPTRDIFF_MAXIMUM PTRDIFF_MAX -#else -#define YYPTRDIFF_T long -#define YYPTRDIFF_MAXIMUM LONG_MAX -#endif -#endif - -#ifndef YYSIZE_T -#ifdef __SIZE_TYPE__ -#define YYSIZE_T __SIZE_TYPE__ -#elif defined size_t -#define YYSIZE_T size_t -#elif defined __STDC_VERSION__ && 199901 <= __STDC_VERSION__ -#include /* INFRINGES ON USER NAME SPACE */ -#define YYSIZE_T size_t -#else -#define YYSIZE_T unsigned -#endif -#endif - -#define YYSIZE_MAXIMUM \ - YY_CAST(YYPTRDIFF_T, (YYPTRDIFF_MAXIMUM < YY_CAST(YYSIZE_T, -1) ? YYPTRDIFF_MAXIMUM : YY_CAST(YYSIZE_T, -1))) - -#define YYSIZEOF(X) YY_CAST(YYPTRDIFF_T, sizeof(X)) - -/* Stored state numbers (used for stacks). */ -typedef yytype_uint8 yy_state_t; - -/* State numbers in computations. */ -typedef int yy_state_fast_t; - -#ifndef YY_ -#if defined YYENABLE_NLS && YYENABLE_NLS -#if ENABLE_NLS -#include /* INFRINGES ON USER NAME SPACE */ -#define YY_(Msgid) dgettext("bison-runtime", Msgid) -#endif -#endif -#ifndef YY_ -#define YY_(Msgid) Msgid -#endif -#endif - -#ifndef YY_ATTRIBUTE_PURE -#if defined __GNUC__ && 2 < __GNUC__ + (96 <= __GNUC_MINOR__) -#define YY_ATTRIBUTE_PURE __attribute__((__pure__)) -#else -#define YY_ATTRIBUTE_PURE -#endif -#endif - -#ifndef YY_ATTRIBUTE_UNUSED -#if defined __GNUC__ && 2 < __GNUC__ + (7 <= __GNUC_MINOR__) -#define YY_ATTRIBUTE_UNUSED __attribute__((__unused__)) -#else -#define YY_ATTRIBUTE_UNUSED -#endif -#endif - -/* Suppress unused-variable warnings by "using" E. */ -#if !defined lint || defined __GNUC__ -#define YY_USE(E) ((void)(E)) -#else -#define YY_USE(E) /* empty */ -#endif - -/* Suppress an incorrect diagnostic about yylval being uninitialized. */ -#if defined __GNUC__ && !defined __ICC && 406 <= __GNUC__ * 100 + __GNUC_MINOR__ -#if __GNUC__ * 100 + __GNUC_MINOR__ < 407 -#define YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN \ - _Pragma("GCC diagnostic push") _Pragma("GCC diagnostic ignored \"-Wuninitialized\"") -#else -#define YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN \ - _Pragma("GCC diagnostic push") _Pragma("GCC diagnostic ignored \"-Wuninitialized\"") \ - _Pragma("GCC diagnostic ignored \"-Wmaybe-uninitialized\"") -#endif -#define YY_IGNORE_MAYBE_UNINITIALIZED_END _Pragma("GCC diagnostic pop") -#else -#define YY_INITIAL_VALUE(Value) Value -#endif -#ifndef YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN -#define YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN -#define YY_IGNORE_MAYBE_UNINITIALIZED_END -#endif -#ifndef YY_INITIAL_VALUE -#define YY_INITIAL_VALUE(Value) /* Nothing. */ -#endif - -#if defined __cplusplus && defined __GNUC__ && !defined __ICC && 6 <= __GNUC__ -#define YY_IGNORE_USELESS_CAST_BEGIN _Pragma("GCC diagnostic push") _Pragma("GCC diagnostic ignored \"-Wuseless-cast\"") -#define YY_IGNORE_USELESS_CAST_END _Pragma("GCC diagnostic pop") -#endif -#ifndef YY_IGNORE_USELESS_CAST_BEGIN -#define YY_IGNORE_USELESS_CAST_BEGIN -#define YY_IGNORE_USELESS_CAST_END -#endif - -#define YY_ASSERT(E) ((void)(0 && (E))) - -#if !defined yyoverflow - -/* The parser invokes alloca or malloc; define the necessary symbols. */ - -#ifdef YYSTACK_USE_ALLOCA -#if YYSTACK_USE_ALLOCA -#ifdef __GNUC__ -#define YYSTACK_ALLOC __builtin_alloca -#elif defined __BUILTIN_VA_ARG_INCR -#include /* INFRINGES ON USER NAME SPACE */ -#elif defined _AIX -#define YYSTACK_ALLOC __alloca -#elif defined _MSC_VER -#include /* INFRINGES ON USER NAME SPACE */ -#define alloca _alloca -#else -#define YYSTACK_ALLOC alloca -#if !defined _ALLOCA_H && !defined EXIT_SUCCESS -#include /* INFRINGES ON USER NAME SPACE */ -/* Use EXIT_SUCCESS as a witness for stdlib.h. */ -#ifndef EXIT_SUCCESS -#define EXIT_SUCCESS 0 -#endif -#endif -#endif -#endif -#endif - -#ifdef YYSTACK_ALLOC -/* Pacify GCC's 'empty if-body' warning. */ -#define YYSTACK_FREE(Ptr) \ - do { /* empty */ \ - ; \ - } while (0) -#ifndef YYSTACK_ALLOC_MAXIMUM -/* The OS might guarantee only one guard page at the bottom of the stack, - and a page size can be as small as 4096 bytes. So we cannot safely - invoke alloca (N) if N exceeds 4096. Use a slightly smaller number - to allow for a few compiler-allocated temporary stack slots. */ -#define YYSTACK_ALLOC_MAXIMUM 4032 /* reasonable circa 2006 */ -#endif -#else -#define YYSTACK_ALLOC YYMALLOC -#define YYSTACK_FREE YYFREE -#ifndef YYSTACK_ALLOC_MAXIMUM -#define YYSTACK_ALLOC_MAXIMUM YYSIZE_MAXIMUM -#endif -#if (defined __cplusplus && !defined EXIT_SUCCESS && \ - !((defined YYMALLOC || defined malloc) && (defined YYFREE || defined free))) -#include /* INFRINGES ON USER NAME SPACE */ -#ifndef EXIT_SUCCESS -#define EXIT_SUCCESS 0 -#endif -#endif -#ifndef YYMALLOC -#define YYMALLOC malloc -#if !defined malloc && !defined EXIT_SUCCESS -void *malloc(YYSIZE_T); /* INFRINGES ON USER NAME SPACE */ -#endif -#endif -#ifndef YYFREE -#define YYFREE free -#if !defined free && !defined EXIT_SUCCESS -void free(void *); /* INFRINGES ON USER NAME SPACE */ -#endif -#endif -#endif -#endif /* !defined yyoverflow */ - -#if (!defined yyoverflow && (!defined __cplusplus || (defined YYSTYPE_IS_TRIVIAL && YYSTYPE_IS_TRIVIAL))) - -/* A type that is properly aligned for any stack member. */ -union yyalloc { - yy_state_t yyss_alloc; - YYSTYPE yyvs_alloc; -}; - -/* The size of the maximum gap between one aligned stack and the next. */ -#define YYSTACK_GAP_MAXIMUM (YYSIZEOF(union yyalloc) - 1) - -/* The size of an array large to enough to hold all stacks, each with - N elements. */ -#define YYSTACK_BYTES(N) ((N) * (YYSIZEOF(yy_state_t) + YYSIZEOF(YYSTYPE)) + YYSTACK_GAP_MAXIMUM) - -#define YYCOPY_NEEDED 1 - -/* Relocate STACK from its old location to the new one. The - local variables YYSIZE and YYSTACKSIZE give the old and new number of - elements in the stack, and YYPTR gives the new location of the - stack. Advance YYPTR to a properly aligned location for the next - stack. */ -#define YYSTACK_RELOCATE(Stack_alloc, Stack) \ - do { \ - YYPTRDIFF_T yynewbytes; \ - YYCOPY(&yyptr->Stack_alloc, Stack, yysize); \ - Stack = &yyptr->Stack_alloc; \ - yynewbytes = yystacksize * YYSIZEOF(*Stack) + YYSTACK_GAP_MAXIMUM; \ - yyptr += yynewbytes / YYSIZEOF(*yyptr); \ - } while (0) - -#endif - -#if defined YYCOPY_NEEDED && YYCOPY_NEEDED -/* Copy COUNT objects from SRC to DST. The source and destination do - not overlap. */ -#ifndef YYCOPY -#if defined __GNUC__ && 1 < __GNUC__ -#define YYCOPY(Dst, Src, Count) __builtin_memcpy(Dst, Src, YY_CAST(YYSIZE_T, (Count)) * sizeof(*(Src))) -#else -#define YYCOPY(Dst, Src, Count) \ - do { \ - YYPTRDIFF_T yyi; \ - for (yyi = 0; yyi < (Count); yyi++) \ - (Dst)[yyi] = (Src)[yyi]; \ - } while (0) -#endif -#endif -#endif /* !YYCOPY_NEEDED */ - -/* YYFINAL -- State number of the termination state. */ -#define YYFINAL 22 -/* YYLAST -- Last index in YYTABLE. */ -#define YYLAST 221 - -/* YYNTOKENS -- Number of terminals. */ -#define YYNTOKENS 41 -/* YYNNTS -- Number of nonterminals. */ -#define YYNNTS 19 -/* YYNRULES -- Number of rules. */ -#define YYNRULES 72 -/* YYNSTATES -- Number of states. */ -#define YYNSTATES 165 - -/* YYMAXUTOK -- Last valid token kind. */ -#define YYMAXUTOK 279 - -/* YYTRANSLATE(TOKEN-NUM) -- Symbol number corresponding to TOKEN-NUM - as returned by yylex, with out-of-bounds checking. */ -#define YYTRANSLATE(YYX) \ - (0 <= (YYX) && (YYX) <= YYMAXUTOK ? YY_CAST(yysymbol_kind_t, yytranslate[YYX]) : YYSYMBOL_YYUNDEF) - -/* YYTRANSLATE[TOKEN-NUM] -- Symbol number corresponding to TOKEN-NUM - as returned by yylex. */ -static const yytype_int8 yytranslate[] = { - 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 39, 2, 2, 2, 2, 2, 2, 34, 35, 30, 26, 33, 25, 2, 31, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 36, 40, 28, 27, 29, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 37, 2, 38, 32, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24}; - -#if YYDEBUG -/* YYRLINE[YYN] -- Source line where rule number YYN was defined. */ -static const yytype_uint8 yyrline[] = {0, 86, 86, 87, 88, 89, 90, 91, 92, 93, 94, 98, 103, 104, 105, - 112, 113, 114, 115, 116, 120, 127, 131, 132, 136, 140, 141, 142, 143, 146, - 147, 148, 149, 153, 154, 155, 160, 165, 166, 167, 168, 172, 173, 174, 175, - 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, - 191, 192, 193, 194, 198, 199, 201, 206, 207, 212, 213, 218, 219}; -#endif - -/** Accessing symbol of state STATE. */ -#define YY_ACCESSING_SYMBOL(State) YY_CAST(yysymbol_kind_t, yystos[State]) - -#if YYDEBUG || 0 -/* The user-facing name of the symbol whose (internal) number is - YYSYMBOL. No bounds checking. */ -static const char *yysymbol_name(yysymbol_kind_t yysymbol) YY_ATTRIBUTE_UNUSED; - -/* YYTNAME[SYMBOL-NUM] -- String name of the symbol SYMBOL-NUM. - First, the terminals, then, starting at YYNTOKENS, nonterminals. */ -static const char *const yytname[] = {"\"end of file\"", - "error", - "\"invalid token\"", - "DPTT_aux", - "DPTT_table", - "DPTT_level", - "DPTT_init", - "DPTT_constant", - "DPTT_eoq", - "DPTT_groupstar", - "DPTT_specs", - "DPTT_save", - "DPTT_and", - "DPTT_macro", - "DPTT_end_of_macro", - "DPTT_or", - "DPTT_not", - "DPTT_ge", - "DPTT_le", - "DPTT_ne", - "DPTT_number", - "DPTT_symbol", - "DPTT_units_symbol", - "DPTT_function", - "DPTT_dt_to_one", - "'-'", - "'+'", - "'='", - "'<'", - "'>'", - "'*'", - "'/'", - "'^'", - "','", - "'('", - "')'", - "':'", - "'['", - "']'", - "'!'", - "';'", - "$accept", - "fulleq", - "teqn", - "tabledef", - "eqn", - "stock_eqn", - "lhs", - "var", - "sublist", - "symlist", - "subdef", - "number", - "maplist", - "exprlist", - "exp", - "tablevals", - "xytablevals", - "xytablevec", - "tablepairs", - YY_NULLPTR}; - -static const char *yysymbol_name(yysymbol_kind_t yysymbol) { - return yytname[yysymbol]; -} -#endif - -#define YYPACT_NINF (-67) - -#define yypact_value_is_default(Yyn) ((Yyn) == YYPACT_NINF) - -#define YYTABLE_NINF (-1) - -#define yytable_value_is_error(Yyn) 0 - -/* YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing - STATE-NUM. */ -static const yytype_int16 yypact[] = { - 208, 10, 32, 32, 10, 10, -67, -67, -67, -67, 63, 41, 66, -14, -67, 53, 83, 74, 102, 93, 114, - 116, -67, -19, 122, -67, -67, 79, 72, -67, 50, -67, 32, -67, -67, -67, 123, 109, 96, 19, 79, -67, - 111, 79, 79, 79, 112, 18, 100, -67, 127, 135, 50, 124, -67, 128, 129, 132, 133, 1, -67, 130, 137, - -13, -67, -67, 136, -67, 144, 159, 121, 121, 54, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, - 79, 79, 79, 79, 79, -67, -67, 134, 50, -67, -67, 50, 143, 50, 50, 79, 147, -67, 157, 148, -67, - -24, -67, -21, 100, 100, 29, 142, -2, -2, -2, 121, 121, -2, -2, -2, 144, 144, 144, 50, 150, -67, - 50, -67, -67, 18, 146, 161, -67, 175, -67, -67, 153, 50, 156, -67, 169, -67, -67, 162, 50, 164, 167, - 168, -67, 170, -67, 50, 172, 50, 171, 70, 173, 187, 78, 30, 188, 132, 133, 173, 133}; - -/* YYDEFACT[STATE-NUM] -- Default reduction number in state STATE-NUM. - Performed when YYTABLE does not specify something else to do. Zero - means the default is an error. */ -static const yytype_int8 yydefact[] = { - 0, 0, 0, 0, 0, 0, 2, 3, 4, 5, 0, 22, 0, 18, 21, 22, 0, 0, 0, 0, 0, 0, 1, 0, 0, 23, 10, 0, - 0, 6, 0, 9, 0, 8, 7, 29, 0, 36, 25, 0, 0, 41, 0, 0, 0, 0, 42, 15, 37, 33, 0, 0, 0, 0, 69, 0, - 0, 67, 64, 11, 12, 0, 0, 0, 19, 26, 0, 24, 59, 0, 61, 62, 0, 0, 0, 40, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 34, 35, 0, 0, 16, 17, 0, 0, 0, 0, 0, 0, 31, 0, 27, 47, 0, 44, 0, 38, 39, 58, 57, - 55, 53, 56, 49, 48, 60, 52, 54, 50, 51, 63, 0, 0, 70, 0, 14, 13, 20, 0, 0, 28, 0, 45, 43, 0, 0, 0, 30, - 0, 46, 71, 0, 0, 0, 0, 0, 32, 0, 72, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 68, 65, 0, 66}; - -/* YYPGOTO[NTERM-NUM]. */ -static const yytype_int8 yypgoto[] = {-67, -67, -67, -67, 13, -67, 85, 22, -67, -67, - -67, -30, -67, -66, -39, -67, -67, 43, -22}; - -/* YYDEFGOTO[NTERM-NUM]. */ -static const yytype_int8 yydefgoto[] = {0, 10, 16, 59, 12, 18, 13, 46, 25, 39, 37, 54, 64, 47, 48, 55, 56, 57, 58}; - -/* YYTABLE[YYPACT[STATE-NUM]] -- What to do in state STATE-NUM. If - positive, shift that token. If negative, reduce the rule whose - number is the opposite. If YYTABLE_NINF, syntax error. */ -static const yytype_uint8 yytable[] = { - 60, 68, 35, 105, 70, 71, 72, 107, 101, 133, 76, 134, 74, 27, 135, 36, 75, 20, 21, 75, 28, 102, 91, - 14, 14, 14, 14, 14, 86, 87, 88, 11, 97, 129, 98, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, - 119, 120, 121, 122, 49, 74, 66, 15, 61, 50, 51, 67, 75, 86, 87, 88, 124, 22, 52, 125, 76, 127, 128, - 77, 49, 78, 79, 80, 26, 50, 51, 23, 24, 81, 82, 83, 84, 85, 86, 87, 88, 17, 19, 106, 24, 29, - 49, 136, 108, 40, 138, 50, 51, 41, 15, 30, 42, 156, 43, 44, 52, 143, 157, 53, 31, 96, 76, 45, 147, - 77, 160, 78, 79, 80, 32, 152, 33, 154, 34, 81, 82, 83, 84, 85, 86, 87, 88, 76, 158, 65, 77, 162, - 78, 79, 80, 164, 63, 38, 62, 69, 73, 89, 83, 84, 85, 86, 87, 88, 76, 90, 99, 103, 92, 78, 79, - 80, 100, 93, 94, 95, 96, 123, 130, 83, 84, 85, 86, 87, 88, 40, 88, 126, 131, 41, 15, 139, 42, 137, - 43, 44, 140, 132, 142, 144, 145, 40, 149, 45, 104, 41, 15, 146, 42, 148, 43, 44, 161, 150, 151, 153, 155, - 52, 0, 45, 141, 1, 2, 3, 4, 5, 6, 7, 8, 9, 159, 163}; - -static const yytype_int16 yycheck[] = { - 30, 40, 21, 69, 43, 44, 45, 73, 21, 33, 12, 35, 33, 27, 35, 34, 40, 4, 5, 40, 34, 34, 52, 1, 2, - 3, 4, 5, 30, 31, 32, 21, 31, 99, 33, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, - 20, 33, 33, 21, 32, 25, 26, 38, 40, 30, 31, 32, 92, 0, 34, 95, 12, 97, 98, 15, 20, 17, 18, 19, 8, - 25, 26, 36, 37, 25, 26, 27, 28, 29, 30, 31, 32, 2, 3, 35, 37, 8, 20, 123, 133, 16, 126, 25, 26, 20, - 21, 27, 23, 33, 25, 26, 34, 137, 38, 37, 8, 33, 12, 34, 144, 15, 38, 17, 18, 19, 27, 151, 8, 153, 8, - 25, 26, 27, 28, 29, 30, 31, 32, 12, 156, 39, 15, 159, 17, 18, 19, 163, 33, 21, 21, 34, 34, 20, 27, 28, - 29, 30, 31, 32, 12, 20, 26, 21, 34, 17, 18, 19, 25, 35, 35, 33, 33, 33, 21, 27, 28, 29, 30, 31, 32, - 16, 32, 34, 21, 20, 21, 35, 23, 33, 25, 26, 25, 39, 35, 33, 21, 16, 25, 34, 35, 20, 21, 35, 23, 35, - 25, 26, 159, 35, 34, 33, 35, 34, -1, 34, 35, 3, 4, 5, 6, 7, 8, 9, 10, 11, 33, 33}; - -/* YYSTOS[STATE-NUM] -- The symbol kind of the accessing symbol of - state STATE-NUM. */ -static const yytype_int8 yystos[] = { - 0, 3, 4, 5, 6, 7, 8, 9, 10, 11, 42, 21, 45, 47, 48, 21, 43, 47, 46, 47, 45, 45, 0, 36, 37, 49, 8, 27, - 34, 8, 27, 8, 27, 8, 8, 21, 34, 51, 21, 50, 16, 20, 23, 25, 26, 34, 48, 54, 55, 20, 25, 26, 34, 37, 52, 56, - 57, 58, 59, 44, 52, 48, 21, 33, 53, 39, 33, 38, 55, 34, 55, 55, 55, 34, 33, 40, 12, 15, 17, 18, 19, 25, 26, 27, - 28, 29, 30, 31, 32, 20, 20, 52, 34, 35, 35, 33, 33, 31, 33, 26, 25, 21, 34, 21, 35, 54, 35, 54, 55, 55, 55, 55, - 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 33, 52, 52, 34, 52, 52, 54, 21, 21, 39, 33, 35, 35, 52, 33, 52, 35, - 25, 35, 35, 52, 33, 21, 35, 52, 35, 25, 35, 34, 52, 33, 52, 35, 33, 38, 59, 33, 38, 58, 59, 33, 59}; - -/* YYR1[RULE-NUM] -- Symbol kind of the left-hand side of rule RULE-NUM. */ -static const yytype_int8 yyr1[] = {0, 41, 42, 42, 42, 42, 42, 42, 42, 42, 42, 43, 44, 44, 44, 45, 45, 45, 45, - 45, 46, 47, 48, 48, 49, 50, 50, 50, 50, 51, 51, 51, 51, 52, 52, 52, 53, 54, - 54, 54, 54, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, - 55, 55, 55, 55, 55, 55, 55, 56, 56, 56, 57, 57, 58, 58, 59, 59}; - -/* YYR2[RULE-NUM] -- Number of symbols on the right-hand side of rule RULE-NUM. */ -static const yytype_int8 yyr2[] = {0, 2, 1, 1, 1, 1, 3, 3, 3, 3, 3, 3, 1, 3, 3, 3, 4, 4, 1, 4, 5, 1, 1, 2, 3, - 1, 2, 3, 4, 1, 5, 3, 7, 1, 2, 2, 0, 1, 3, 3, 2, 1, 1, 4, 3, 4, 5, 3, 3, 3, - 3, 3, 3, 3, 3, 3, 3, 3, 3, 2, 3, 2, 2, 3, 1, 15, 17, 1, 15, 1, 3, 5, 7}; - -enum { YYENOMEM = -2 }; - -#define yyerrok (yyerrstatus = 0) -#define yyclearin (yychar = YYEMPTY) - -#define YYACCEPT goto yyacceptlab -#define YYABORT goto yyabortlab -#define YYERROR goto yyerrorlab -#define YYNOMEM goto yyexhaustedlab - -#define YYRECOVERING() (!!yyerrstatus) - -#define YYBACKUP(Token, Value) \ - do \ - if (yychar == YYEMPTY) { \ - yychar = (Token); \ - yylval = (Value); \ - YYPOPSTACK(yylen); \ - yystate = *yyssp; \ - goto yybackup; \ - } else { \ - yyerror(YY_("syntax error: cannot back up")); \ - YYERROR; \ - } \ - while (0) - -/* Backward compatibility with an undocumented macro. - Use YYerror or YYUNDEF. */ -#define YYERRCODE YYUNDEF - -/* Enable debugging if requested. */ -#if YYDEBUG - -#ifndef YYFPRINTF -#include /* INFRINGES ON USER NAME SPACE */ -#define YYFPRINTF fprintf -#endif - -#define YYDPRINTF(Args) \ - do { \ - if (yydebug) \ - YYFPRINTF Args; \ - } while (0) - -#define YY_SYMBOL_PRINT(Title, Kind, Value, Location) \ - do { \ - if (yydebug) { \ - YYFPRINTF(stderr, "%s ", Title); \ - yy_symbol_print(stderr, Kind, Value); \ - YYFPRINTF(stderr, "\n"); \ - } \ - } while (0) - -/*-----------------------------------. -| Print this symbol's value on YYO. | -`-----------------------------------*/ - -static void yy_symbol_value_print(FILE *yyo, yysymbol_kind_t yykind, YYSTYPE const *const yyvaluep) { - FILE *yyoutput = yyo; - YY_USE(yyoutput); - if (!yyvaluep) - return; - YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN - YY_USE(yykind); - YY_IGNORE_MAYBE_UNINITIALIZED_END -} - -/*---------------------------. -| Print this symbol on YYO. | -`---------------------------*/ - -static void yy_symbol_print(FILE *yyo, yysymbol_kind_t yykind, YYSTYPE const *const yyvaluep) { - YYFPRINTF(yyo, "%s %s (", yykind < YYNTOKENS ? "token" : "nterm", yysymbol_name(yykind)); - - yy_symbol_value_print(yyo, yykind, yyvaluep); - YYFPRINTF(yyo, ")"); -} - -/*------------------------------------------------------------------. -| yy_stack_print -- Print the state stack from its BOTTOM up to its | -| TOP (included). | -`------------------------------------------------------------------*/ - -static void yy_stack_print(yy_state_t *yybottom, yy_state_t *yytop) { - YYFPRINTF(stderr, "Stack now"); - for (; yybottom <= yytop; yybottom++) { - int yybot = *yybottom; - YYFPRINTF(stderr, " %d", yybot); - } - YYFPRINTF(stderr, "\n"); -} - -#define YY_STACK_PRINT(Bottom, Top) \ - do { \ - if (yydebug) \ - yy_stack_print((Bottom), (Top)); \ - } while (0) - -/*------------------------------------------------. -| Report that the YYRULE is going to be reduced. | -`------------------------------------------------*/ - -static void yy_reduce_print(yy_state_t *yyssp, YYSTYPE *yyvsp, int yyrule) { - int yylno = yyrline[yyrule]; - int yynrhs = yyr2[yyrule]; - int yyi; - YYFPRINTF(stderr, "Reducing stack by rule %d (line %d):\n", yyrule - 1, yylno); - /* The symbols being reduced. */ - for (yyi = 0; yyi < yynrhs; yyi++) { - YYFPRINTF(stderr, " $%d = ", yyi + 1); - yy_symbol_print(stderr, YY_ACCESSING_SYMBOL(+yyssp[yyi + 1 - yynrhs]), &yyvsp[(yyi + 1) - (yynrhs)]); - YYFPRINTF(stderr, "\n"); - } -} - -#define YY_REDUCE_PRINT(Rule) \ - do { \ - if (yydebug) \ - yy_reduce_print(yyssp, yyvsp, Rule); \ - } while (0) - -/* Nonzero means print parse trace. It is left uninitialized so that - multiple parsers can coexist. */ -int yydebug; -#else /* !YYDEBUG */ -#define YYDPRINTF(Args) ((void)0) -#define YY_SYMBOL_PRINT(Title, Kind, Value, Location) -#define YY_STACK_PRINT(Bottom, Top) -#define YY_REDUCE_PRINT(Rule) -#endif /* !YYDEBUG */ - -/* YYINITDEPTH -- initial size of the parser's stacks. */ -#ifndef YYINITDEPTH -#define YYINITDEPTH 200 -#endif - -/* YYMAXDEPTH -- maximum size the stacks can grow to (effective only - if the built-in stack extension method is used). - - Do not make this value too large; the results are undefined if - YYSTACK_ALLOC_MAXIMUM < YYSTACK_BYTES (YYMAXDEPTH) - evaluated with infinite-precision integer arithmetic. */ - -#ifndef YYMAXDEPTH -#define YYMAXDEPTH 10000 -#endif - -/*-----------------------------------------------. -| Release the memory associated to this symbol. | -`-----------------------------------------------*/ - -static void yydestruct(const char *yymsg, yysymbol_kind_t yykind, YYSTYPE *yyvaluep) { - YY_USE(yyvaluep); - if (!yymsg) - yymsg = "Deleting"; - YY_SYMBOL_PRINT(yymsg, yykind, yyvaluep, yylocationp); - - YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN - YY_USE(yykind); - YY_IGNORE_MAYBE_UNINITIALIZED_END -} - -/* Lookahead token kind. */ -int yychar; - -/* The semantic value of the lookahead symbol. */ -YYSTYPE yylval; -/* Number of syntax errors so far. */ -int yynerrs; - -/*----------. -| yyparse. | -`----------*/ - -int yyparse(void) { - yy_state_fast_t yystate = 0; - /* Number of tokens to shift before error messages enabled. */ - int yyerrstatus = 0; - - /* Refer to the stacks through separate pointers, to allow yyoverflow - to reallocate them elsewhere. */ - - /* Their size. */ - YYPTRDIFF_T yystacksize = YYINITDEPTH; - - /* The state stack: array, bottom, top. */ - yy_state_t yyssa[YYINITDEPTH]; - yy_state_t *yyss = yyssa; - yy_state_t *yyssp = yyss; - - /* The semantic value stack: array, bottom, top. */ - YYSTYPE yyvsa[YYINITDEPTH]; - YYSTYPE *yyvs = yyvsa; - YYSTYPE *yyvsp = yyvs; - - int yyn; - /* The return value of yyparse. */ - int yyresult; - /* Lookahead symbol kind. */ - yysymbol_kind_t yytoken = YYSYMBOL_YYEMPTY; - /* The variables used to return semantic value and location from the - action routines. */ - YYSTYPE yyval; - -#define YYPOPSTACK(N) (yyvsp -= (N), yyssp -= (N)) - - /* The number of symbols on the RHS of the reduced rule. - Keep to zero when no symbol should be popped. */ - int yylen = 0; - - YYDPRINTF((stderr, "Starting parse\n")); - - yychar = YYEMPTY; /* Cause a token to be read. */ - - goto yysetstate; - -/*------------------------------------------------------------. -| yynewstate -- push a new state, which is found in yystate. | -`------------------------------------------------------------*/ -yynewstate: - /* In all cases, when you get here, the value and location stacks - have just been pushed. So pushing a state here evens the stacks. */ - yyssp++; - -/*--------------------------------------------------------------------. -| yysetstate -- set current state (the top of the stack) to yystate. | -`--------------------------------------------------------------------*/ -yysetstate: - YYDPRINTF((stderr, "Entering state %d\n", yystate)); - YY_ASSERT(0 <= yystate && yystate < YYNSTATES); - YY_IGNORE_USELESS_CAST_BEGIN - *yyssp = YY_CAST(yy_state_t, yystate); - YY_IGNORE_USELESS_CAST_END - YY_STACK_PRINT(yyss, yyssp); - - if (yyss + yystacksize - 1 <= yyssp) -#if !defined yyoverflow && !defined YYSTACK_RELOCATE - YYNOMEM; -#else - { - /* Get the current used size of the three stacks, in elements. */ - YYPTRDIFF_T yysize = yyssp - yyss + 1; - -#if defined yyoverflow - { - /* Give user a chance to reallocate the stack. Use copies of - these so that the &'s don't force the real ones into - memory. */ - yy_state_t *yyss1 = yyss; - YYSTYPE *yyvs1 = yyvs; - - /* Each stack pointer address is followed by the size of the - data in use in that stack, in bytes. This used to be a - conditional around just the two extra args, but that might - be undefined if yyoverflow is a macro. */ - yyoverflow(YY_("memory exhausted"), &yyss1, yysize * YYSIZEOF(*yyssp), &yyvs1, yysize * YYSIZEOF(*yyvsp), - &yystacksize); - yyss = yyss1; - yyvs = yyvs1; - } -#else /* defined YYSTACK_RELOCATE */ - /* Extend the stack our own way. */ - if (YYMAXDEPTH <= yystacksize) - YYNOMEM; - yystacksize *= 2; - if (YYMAXDEPTH < yystacksize) - yystacksize = YYMAXDEPTH; - - { - yy_state_t *yyss1 = yyss; - union yyalloc *yyptr = YY_CAST(union yyalloc *, YYSTACK_ALLOC(YY_CAST(YYSIZE_T, YYSTACK_BYTES(yystacksize)))); - if (!yyptr) - YYNOMEM; - YYSTACK_RELOCATE(yyss_alloc, yyss); - YYSTACK_RELOCATE(yyvs_alloc, yyvs); -#undef YYSTACK_RELOCATE - if (yyss1 != yyssa) - YYSTACK_FREE(yyss1); - } -#endif - - yyssp = yyss + yysize - 1; - yyvsp = yyvs + yysize - 1; - - YY_IGNORE_USELESS_CAST_BEGIN - YYDPRINTF((stderr, "Stack size increased to %ld\n", YY_CAST(long, yystacksize))); - YY_IGNORE_USELESS_CAST_END - - if (yyss + yystacksize - 1 <= yyssp) - YYABORT; - } -#endif /* !defined yyoverflow && !defined YYSTACK_RELOCATE */ - - if (yystate == YYFINAL) - YYACCEPT; - - goto yybackup; - -/*-----------. -| yybackup. | -`-----------*/ -yybackup: - /* Do appropriate processing given the current state. Read a - lookahead token if we need one and don't already have one. */ - - /* First try to decide what to do without reference to lookahead token. */ - yyn = yypact[yystate]; - if (yypact_value_is_default(yyn)) - goto yydefault; - - /* Not known => get a lookahead token if don't already have one. */ - - /* YYCHAR is either empty, or end-of-input, or a valid lookahead. */ - if (yychar == YYEMPTY) { - YYDPRINTF((stderr, "Reading a token\n")); - yychar = yylex(); - } - - if (yychar <= YYEOF) { - yychar = YYEOF; - yytoken = YYSYMBOL_YYEOF; - YYDPRINTF((stderr, "Now at end of input.\n")); - } else if (yychar == YYerror) { - /* The scanner already issued an error message, process directly - to error recovery. But do not keep the error token as - lookahead, it is too special and may lead us to an endless - loop in error recovery. */ - yychar = YYUNDEF; - yytoken = YYSYMBOL_YYerror; - goto yyerrlab1; - } else { - yytoken = YYTRANSLATE(yychar); - YY_SYMBOL_PRINT("Next token is", yytoken, &yylval, &yylloc); - } - - /* If the proper action on seeing token YYTOKEN is to reduce or to - detect an error, take that action. */ - yyn += yytoken; - if (yyn < 0 || YYLAST < yyn || yycheck[yyn] != yytoken) - goto yydefault; - yyn = yytable[yyn]; - if (yyn <= 0) { - if (yytable_value_is_error(yyn)) - goto yyerrlab; - yyn = -yyn; - goto yyreduce; - } - - /* Count tokens shifted since error; after three, turn off error - status. */ - if (yyerrstatus) - yyerrstatus--; - - /* Shift the lookahead token. */ - YY_SYMBOL_PRINT("Shifting", yytoken, &yylval, &yylloc); - yystate = yyn; - YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN - *++yyvsp = yylval; - YY_IGNORE_MAYBE_UNINITIALIZED_END - - /* Discard the shifted token. */ - yychar = YYEMPTY; - goto yynewstate; - -/*-----------------------------------------------------------. -| yydefault -- do the default action for the current state. | -`-----------------------------------------------------------*/ -yydefault: - yyn = yydefact[yystate]; - if (yyn == 0) - goto yyerrlab; - goto yyreduce; - -/*-----------------------------. -| yyreduce -- do a reduction. | -`-----------------------------*/ -yyreduce: - /* yyn is the number of a rule to reduce with. */ - yylen = yyr2[yyn]; - - /* If YYLEN is nonzero, implement the default value of the action: - '$$ = $1'. - - Otherwise, the following line sets YYVAL to garbage. - This behavior is undocumented and Bison - users should not rely upon it. Assigning to YYVAL - unconditionally makes the parser a bit smaller, and it avoids a - GCC warning that YYVAL may be used uninitialized. */ - yyval = yyvsp[1 - yylen]; - - YY_REDUCE_PRINT(yyn); - switch (yyn) { - case 2: /* fulleq: DPTT_eoq */ -#line 86 "DYacc.y" - { - return DPTT_eoq; - } -#line 1246 "DYacc.tab.cpp" - break; - - case 3: /* fulleq: DPTT_groupstar */ -#line 87 "DYacc.y" - { - return DPTT_groupstar; - } -#line 1252 "DYacc.tab.cpp" - break; - - case 4: /* fulleq: DPTT_specs */ -#line 88 "DYacc.y" - { - return DPTT_specs; - } -#line 1258 "DYacc.tab.cpp" - break; - - case 5: /* fulleq: DPTT_save */ -#line 89 "DYacc.y" - { - return DPTT_save; - } -#line 1264 "DYacc.tab.cpp" - break; - - case 6: /* fulleq: DPTT_table teqn DPTT_eoq */ -#line 90 "DYacc.y" - { - dpyy_addfulleq((yyvsp[-1].eqn), DPTT_table); - return DPTT_eoq; - } -#line 1270 "DYacc.tab.cpp" - break; - - case 7: /* fulleq: DPTT_constant eqn DPTT_eoq */ -#line 91 "DYacc.y" - { - dpyy_addfulleq((yyvsp[-1].eqn), DPTT_constant); - return DPTT_eoq; - } -#line 1276 "DYacc.tab.cpp" - break; - - case 8: /* fulleq: DPTT_init eqn DPTT_eoq */ -#line 92 "DYacc.y" - { - dpyy_addfulleq((yyvsp[-1].eqn), DPTT_init); - return DPTT_eoq; - } -#line 1282 "DYacc.tab.cpp" - break; - - case 9: /* fulleq: DPTT_level stock_eqn DPTT_eoq */ -#line 93 "DYacc.y" - { - dpyy_addfulleq((yyvsp[-1].eqn), DPTT_level); - return DPTT_eoq; - } -#line 1288 "DYacc.tab.cpp" - break; - - case 10: /* fulleq: DPTT_aux eqn DPTT_eoq */ -#line 94 "DYacc.y" - { - dpyy_addfulleq((yyvsp[-1].eqn), DPTT_aux); - return DPTT_eoq; - } -#line 1294 "DYacc.tab.cpp" - break; - - case 11: /* teqn: lhs '=' tabledef */ -#line 98 "DYacc.y" - { - (yyval.eqn) = dpyy_add_lookup((yyvsp[-2].lhs), NULL, (yyvsp[0].tbl), 0); - } -#line 1300 "DYacc.tab.cpp" - break; - - case 12: /* tabledef: number */ -#line 103 "DYacc.y" - { - (yyval.tbl) = dpyy_tablevec(NULL, (yyvsp[0].num)); - } -#line 1306 "DYacc.tab.cpp" - break; - - case 13: /* tabledef: tabledef ',' number */ -#line 104 "DYacc.y" - { - (yyval.tbl) = dpyy_tablevec((yyvsp[-2].tbl), (yyvsp[0].num)); - } -#line 1312 "DYacc.tab.cpp" - break; - - case 14: /* tabledef: tabledef '/' number */ -#line 105 "DYacc.y" - { - (yyval.tbl) = dpyy_tablevec((yyvsp[-2].tbl), (yyvsp[0].num)); - } -#line 1318 "DYacc.tab.cpp" - break; - - case 15: /* eqn: lhs '=' exprlist */ -#line 112 "DYacc.y" - { - (yyval.eqn) = dpyy_addeq((yyvsp[-2].lhs), NULL, (yyvsp[0].exl), '='); - } -#line 1324 "DYacc.tab.cpp" - break; - - case 16: /* eqn: lhs '(' tablevals ')' */ -#line 113 "DYacc.y" - { - (yyval.eqn) = dpyy_add_lookup((yyvsp[-3].lhs), NULL, (yyvsp[-1].tbl), 0); - } -#line 1330 "DYacc.tab.cpp" - break; - - case 17: /* eqn: lhs '(' xytablevals ')' */ -#line 114 "DYacc.y" - { - (yyval.eqn) = dpyy_add_lookup((yyvsp[-3].lhs), NULL, (yyvsp[-1].tbl), 1); - } -#line 1336 "DYacc.tab.cpp" - break; - - case 18: /* eqn: lhs */ -#line 115 "DYacc.y" - { - (yyval.eqn) = dpyy_add_lookup((yyvsp[0].lhs), NULL, NULL, 0); - } -#line 1342 "DYacc.tab.cpp" - break; - - case 19: /* eqn: DPTT_symbol ':' subdef maplist */ -#line 116 "DYacc.y" - { - (yyval.eqn) = dpyy_addeq(dpyy_addexceptinterp(dpyy_var_expression((yyvsp[-3].sym), NULL), NULL, 0), - (Expression *)dpyy_symlist_expression((yyvsp[-1].sml), (yyvsp[0].sml)), NULL, ':'); - } -#line 1348 "DYacc.tab.cpp" - break; - - case 20: /* stock_eqn: lhs '=' var '+' exprlist */ -#line 120 "DYacc.y" - { - (yyval.eqn) = dpyy_addstockeq((yyvsp[-4].lhs), (yyvsp[-2].var), (yyvsp[0].exl), '='); - } -#line 1354 "DYacc.tab.cpp" - break; - - case 21: /* lhs: var */ -#line 127 "DYacc.y" - { - (yyval.lhs) = dpyy_addexceptinterp((yyvsp[0].var), NULL, 0); - } -#line 1360 "DYacc.tab.cpp" - break; - - case 22: /* var: DPTT_symbol */ -#line 131 "DYacc.y" - { - (yyval.var) = dpyy_var_expression((yyvsp[0].sym), NULL); - } -#line 1366 "DYacc.tab.cpp" - break; - - case 23: /* var: DPTT_symbol sublist */ -#line 132 "DYacc.y" - { - (yyval.var) = dpyy_var_expression((yyvsp[-1].sym), (yyvsp[0].sml)); - } -#line 1372 "DYacc.tab.cpp" - break; - - case 24: /* sublist: '[' symlist ']' */ -#line 136 "DYacc.y" - { - (yyval.sml) = (yyvsp[-1].sml); - } -#line 1378 "DYacc.tab.cpp" - break; - - case 25: /* symlist: DPTT_symbol */ -#line 140 "DYacc.y" - { - (yyval.sml) = dpyy_symlist(NULL, (yyvsp[0].sym), 0, NULL); - } -#line 1384 "DYacc.tab.cpp" - break; - - case 26: /* symlist: DPTT_symbol '!' */ -#line 141 "DYacc.y" - { - (yyval.sml) = dpyy_symlist(NULL, (yyvsp[-1].sym), 1, NULL); - } -#line 1390 "DYacc.tab.cpp" - break; - - case 27: /* symlist: symlist ',' DPTT_symbol */ -#line 142 "DYacc.y" - { - (yyval.sml) = dpyy_symlist((yyvsp[-2].sml), (yyvsp[0].sym), 0, NULL); - } -#line 1396 "DYacc.tab.cpp" - break; - - case 28: /* symlist: symlist ',' DPTT_symbol '!' */ -#line 143 "DYacc.y" - { - (yyval.sml) = dpyy_symlist((yyvsp[-3].sml), (yyvsp[-1].sym), 1, NULL); - } -#line 1402 "DYacc.tab.cpp" - break; - - case 29: /* subdef: DPTT_symbol */ -#line 146 "DYacc.y" - { - (yyval.sml) = dpyy_symlist(NULL, (yyvsp[0].sym), 0, NULL); - } -#line 1408 "DYacc.tab.cpp" - break; - - case 30: /* subdef: '(' DPTT_symbol '-' DPTT_symbol ')' */ -#line 147 "DYacc.y" - { - (yyval.sml) = dpyy_symlist(NULL, (yyvsp[-3].sym), 0, (yyvsp[-1].sym)); - } -#line 1414 "DYacc.tab.cpp" - break; - - case 31: /* subdef: subdef ',' DPTT_symbol */ -#line 148 "DYacc.y" - { - (yyval.sml) = dpyy_symlist((yyvsp[-2].sml), (yyvsp[0].sym), 0, NULL); - } -#line 1420 "DYacc.tab.cpp" - break; - - case 32: /* subdef: subdef ',' '(' DPTT_symbol '-' DPTT_symbol ')' */ -#line 149 "DYacc.y" - { - (yyval.sml) = dpyy_symlist((yyvsp[-6].sml), (yyvsp[-3].sym), 0, (yyvsp[-1].sym)); - } -#line 1426 "DYacc.tab.cpp" - break; - - case 33: /* number: DPTT_number */ -#line 153 "DYacc.y" - { - (yyval.num) = (yyvsp[0].num); - } -#line 1432 "DYacc.tab.cpp" - break; - - case 34: /* number: '-' DPTT_number */ -#line 154 "DYacc.y" - { - (yyval.num) = -(yyvsp[0].num); - } -#line 1438 "DYacc.tab.cpp" - break; - - case 35: /* number: '+' DPTT_number */ -#line 155 "DYacc.y" - { - (yyval.num) = (yyvsp[0].num); - } -#line 1444 "DYacc.tab.cpp" - break; - - case 36: /* maplist: %empty */ -#line 160 "DYacc.y" - { - (yyval.sml) = NULL; - } -#line 1450 "DYacc.tab.cpp" - break; - - case 37: /* exprlist: exp */ -#line 165 "DYacc.y" - { - (yyval.exl) = dpyy_chain_exprlist(NULL, (yyvsp[0].exn)); - } -#line 1456 "DYacc.tab.cpp" - break; - - case 38: /* exprlist: exprlist ',' exp */ -#line 166 "DYacc.y" - { - (yyval.exl) = dpyy_chain_exprlist((yyvsp[-2].exl), (yyvsp[0].exn)); - } -#line 1462 "DYacc.tab.cpp" - break; - - case 39: /* exprlist: exprlist ';' exp */ -#line 167 "DYacc.y" - { - (yyval.exl) = dpyy_chain_exprlist((yyvsp[-2].exl), (yyvsp[0].exn)); - } -#line 1468 "DYacc.tab.cpp" - break; - - case 40: /* exprlist: exprlist ';' */ -#line 168 "DYacc.y" - { - (yyval.exl) = (yyvsp[-1].exl); - } -#line 1474 "DYacc.tab.cpp" - break; - - case 41: /* exp: DPTT_number */ -#line 172 "DYacc.y" - { - (yyval.exn) = dpyy_num_expression((yyvsp[0].num)); - } -#line 1480 "DYacc.tab.cpp" - break; - - case 42: /* exp: var */ -#line 173 "DYacc.y" - { - (yyval.exn) = (Expression *)(yyvsp[0].var); - } -#line 1486 "DYacc.tab.cpp" - break; - - case 43: /* exp: var '(' exprlist ')' */ -#line 174 "DYacc.y" - { - (yyval.exn) = dpyy_lookup_expression((yyvsp[-3].var), (yyvsp[-1].exl)); - } -#line 1492 "DYacc.tab.cpp" - break; - - case 44: /* exp: '(' exp ')' */ -#line 175 "DYacc.y" - { - (yyval.exn) = dpyy_operator_expression('(', (yyvsp[-1].exn), NULL); - } -#line 1498 "DYacc.tab.cpp" - break; - - case 45: /* exp: DPTT_function '(' exprlist ')' */ -#line 176 "DYacc.y" - { - (yyval.exn) = dpyy_function_expression((yyvsp[-3].fnc), (yyvsp[-1].exl)); - } -#line 1504 "DYacc.tab.cpp" - break; - - case 46: /* exp: DPTT_function '(' exprlist ',' ')' */ -#line 177 "DYacc.y" - { - (yyval.exn) = - dpyy_function_expression((yyvsp[-4].fnc), dpyy_chain_exprlist((yyvsp[-2].exl), dpyy_literal_expression("?"))); - } -#line 1510 "DYacc.tab.cpp" - break; - - case 47: /* exp: DPTT_function '(' ')' */ -#line 178 "DYacc.y" - { - (yyval.exn) = dpyy_function_expression((yyvsp[-2].fnc), NULL); - } -#line 1516 "DYacc.tab.cpp" - break; - - case 48: /* exp: exp '+' exp */ -#line 179 "DYacc.y" - { - (yyval.exn) = dpyy_operator_expression('+', (yyvsp[-2].exn), (yyvsp[0].exn)); - } -#line 1522 "DYacc.tab.cpp" - break; - - case 49: /* exp: exp '-' exp */ -#line 180 "DYacc.y" - { - (yyval.exn) = dpyy_operator_expression('-', (yyvsp[-2].exn), (yyvsp[0].exn)); - } -#line 1528 "DYacc.tab.cpp" - break; - - case 50: /* exp: exp '*' exp */ -#line 181 "DYacc.y" - { - (yyval.exn) = dpyy_operator_expression('*', (yyvsp[-2].exn), (yyvsp[0].exn)); - } -#line 1534 "DYacc.tab.cpp" - break; - - case 51: /* exp: exp '/' exp */ -#line 182 "DYacc.y" - { - (yyval.exn) = dpyy_operator_expression('/', (yyvsp[-2].exn), (yyvsp[0].exn)); - } -#line 1540 "DYacc.tab.cpp" - break; - - case 52: /* exp: exp '<' exp */ -#line 183 "DYacc.y" - { - (yyval.exn) = dpyy_operator_expression('<', (yyvsp[-2].exn), (yyvsp[0].exn)); - } -#line 1546 "DYacc.tab.cpp" - break; - - case 53: /* exp: exp DPTT_le exp */ -#line 184 "DYacc.y" - { - (yyval.exn) = dpyy_operator_expression(DPTT_le, (yyvsp[-2].exn), (yyvsp[0].exn)); - } -#line 1552 "DYacc.tab.cpp" - break; - - case 54: /* exp: exp '>' exp */ -#line 185 "DYacc.y" - { - (yyval.exn) = dpyy_operator_expression('>', (yyvsp[-2].exn), (yyvsp[0].exn)); - } -#line 1558 "DYacc.tab.cpp" - break; - - case 55: /* exp: exp DPTT_ge exp */ -#line 186 "DYacc.y" - { - (yyval.exn) = dpyy_operator_expression(DPTT_ge, (yyvsp[-2].exn), (yyvsp[0].exn)); - } -#line 1564 "DYacc.tab.cpp" - break; - - case 56: /* exp: exp DPTT_ne exp */ -#line 187 "DYacc.y" - { - (yyval.exn) = dpyy_operator_expression(DPTT_ne, (yyvsp[-2].exn), (yyvsp[0].exn)); - } -#line 1570 "DYacc.tab.cpp" - break; - - case 57: /* exp: exp DPTT_or exp */ -#line 188 "DYacc.y" - { - (yyval.exn) = dpyy_operator_expression(DPTT_or, (yyvsp[-2].exn), (yyvsp[0].exn)); - } -#line 1576 "DYacc.tab.cpp" - break; - - case 58: /* exp: exp DPTT_and exp */ -#line 189 "DYacc.y" - { - (yyval.exn) = dpyy_operator_expression(DPTT_and, (yyvsp[-2].exn), (yyvsp[0].exn)); - } -#line 1582 "DYacc.tab.cpp" - break; - - case 59: /* exp: DPTT_not exp */ -#line 190 "DYacc.y" - { - (yyval.exn) = dpyy_operator_expression(DPTT_not, (yyvsp[0].exn), NULL); - } -#line 1588 "DYacc.tab.cpp" - break; - - case 60: /* exp: exp '=' exp */ -#line 191 "DYacc.y" - { - (yyval.exn) = dpyy_operator_expression('=', (yyvsp[-2].exn), (yyvsp[0].exn)); - } -#line 1594 "DYacc.tab.cpp" - break; - - case 61: /* exp: '-' exp */ -#line 192 "DYacc.y" - { - (yyval.exn) = dpyy_operator_expression('-', NULL, (yyvsp[0].exn)); - } -#line 1600 "DYacc.tab.cpp" - break; - - case 62: /* exp: '+' exp */ -#line 193 "DYacc.y" - { - (yyval.exn) = dpyy_operator_expression('+', NULL, (yyvsp[0].exn)); - } -#line 1606 "DYacc.tab.cpp" - break; - - case 63: /* exp: exp '^' exp */ -#line 194 "DYacc.y" - { - (yyval.exn) = dpyy_operator_expression('^', (yyvsp[-2].exn), (yyvsp[0].exn)); - } -#line 1612 "DYacc.tab.cpp" - break; - - case 64: /* tablevals: tablepairs */ -#line 198 "DYacc.y" - { - (yyval.tbl) = (yyvsp[0].tbl); - } -#line 1618 "DYacc.tab.cpp" - break; - - case 65: /* tablevals: '[' '(' number ',' number ')' '-' '(' number ',' number ')' ']' ',' tablepairs */ -#line 200 "DYacc.y" - { - (yyval.tbl) = dpyy_tablerange((yyvsp[0].tbl), (yyvsp[-12].num), (yyvsp[-10].num), (yyvsp[-6].num), (yyvsp[-4].num)); - } -#line 1624 "DYacc.tab.cpp" - break; - - case 66: /* tablevals: '[' '(' number ',' number ')' '-' '(' number ',' number ')' ',' tablepairs ']' ',' tablepairs - */ -#line 202 "DYacc.y" - { - (yyval.tbl) = dpyy_tablerange((yyvsp[0].tbl), (yyvsp[-14].num), (yyvsp[-12].num), (yyvsp[-8].num), (yyvsp[-6].num)); - } -#line 1630 "DYacc.tab.cpp" - break; - - case 67: /* xytablevals: xytablevec */ -#line 206 "DYacc.y" - { - (yyval.tbl) = (yyvsp[0].tbl); - } -#line 1636 "DYacc.tab.cpp" - break; - - case 68: /* xytablevals: '[' '(' number ',' number ')' '-' '(' number ',' number ')' ']' ',' xytablevec */ -#line 208 "DYacc.y" - { - (yyval.tbl) = dpyy_tablerange((yyvsp[0].tbl), (yyvsp[-12].num), (yyvsp[-10].num), (yyvsp[-6].num), (yyvsp[-4].num)); - } -#line 1642 "DYacc.tab.cpp" - break; - - case 69: /* xytablevec: number */ -#line 212 "DYacc.y" - { - (yyval.tbl) = dpyy_tablevec(NULL, (yyvsp[0].num)); - } -#line 1648 "DYacc.tab.cpp" - break; - - case 70: /* xytablevec: xytablevec ',' number */ -#line 213 "DYacc.y" - { - (yyval.tbl) = dpyy_tablevec((yyvsp[-2].tbl), (yyvsp[0].num)); - } -#line 1654 "DYacc.tab.cpp" - break; - - case 71: /* tablepairs: '(' number ',' number ')' */ -#line 218 "DYacc.y" - { - (yyval.tbl) = dpyy_tablepair(NULL, (yyvsp[-3].num), (yyvsp[-1].num)); - } -#line 1660 "DYacc.tab.cpp" - break; - - case 72: /* tablepairs: tablepairs ',' '(' number ',' number ')' */ -#line 219 "DYacc.y" - { - (yyval.tbl) = dpyy_tablepair((yyvsp[-6].tbl), (yyvsp[-3].num), (yyvsp[-1].num)); - } -#line 1666 "DYacc.tab.cpp" - break; - -#line 1670 "DYacc.tab.cpp" - - default: - break; - } - /* User semantic actions sometimes alter yychar, and that requires - that yytoken be updated with the new translation. We take the - approach of translating immediately before every use of yytoken. - One alternative is translating here after every semantic action, - but that translation would be missed if the semantic action invokes - YYABORT, YYACCEPT, or YYERROR immediately after altering yychar or - if it invokes YYBACKUP. In the case of YYABORT or YYACCEPT, an - incorrect destructor might then be invoked immediately. In the - case of YYERROR or YYBACKUP, subsequent parser actions might lead - to an incorrect destructor call or verbose syntax error message - before the lookahead is translated. */ - YY_SYMBOL_PRINT("-> $$ =", YY_CAST(yysymbol_kind_t, yyr1[yyn]), &yyval, &yyloc); - - YYPOPSTACK(yylen); - yylen = 0; - - *++yyvsp = yyval; - - /* Now 'shift' the result of the reduction. Determine what state - that goes to, based on the state we popped back to and the rule - number reduced by. */ - { - const int yylhs = yyr1[yyn] - YYNTOKENS; - const int yyi = yypgoto[yylhs] + *yyssp; - yystate = (0 <= yyi && yyi <= YYLAST && yycheck[yyi] == *yyssp ? yytable[yyi] : yydefgoto[yylhs]); - } - - goto yynewstate; - -/*--------------------------------------. -| yyerrlab -- here on detecting error. | -`--------------------------------------*/ -yyerrlab: - /* Make sure we have latest lookahead translation. See comments at - user semantic actions for why this is necessary. */ - yytoken = yychar == YYEMPTY ? YYSYMBOL_YYEMPTY : YYTRANSLATE(yychar); - /* If not already recovering from an error, report this error. */ - if (!yyerrstatus) { - ++yynerrs; - yyerror(YY_("syntax error")); - } - - if (yyerrstatus == 3) { - /* If just tried and failed to reuse lookahead token after an - error, discard it. */ - - if (yychar <= YYEOF) { - /* Return failure if at end of input. */ - if (yychar == YYEOF) - YYABORT; - } else { - yydestruct("Error: discarding", yytoken, &yylval); - yychar = YYEMPTY; - } - } - - /* Else will try to reuse lookahead token after shifting the error - token. */ - goto yyerrlab1; - -/*---------------------------------------------------. -| yyerrorlab -- error raised explicitly by YYERROR. | -`---------------------------------------------------*/ -yyerrorlab: - /* Pacify compilers when the user code never invokes YYERROR and the - label yyerrorlab therefore never appears in user code. */ - if (0) - YYERROR; - ++yynerrs; - - /* Do not reclaim the symbols of the rule whose action triggered - this YYERROR. */ - YYPOPSTACK(yylen); - yylen = 0; - YY_STACK_PRINT(yyss, yyssp); - yystate = *yyssp; - goto yyerrlab1; - -/*-------------------------------------------------------------. -| yyerrlab1 -- common code for both syntax error and YYERROR. | -`-------------------------------------------------------------*/ -yyerrlab1: - yyerrstatus = 3; /* Each real token shifted decrements this. */ - - /* Pop stack until we find a state that shifts the error token. */ - for (;;) { - yyn = yypact[yystate]; - if (!yypact_value_is_default(yyn)) { - yyn += YYSYMBOL_YYerror; - if (0 <= yyn && yyn <= YYLAST && yycheck[yyn] == YYSYMBOL_YYerror) { - yyn = yytable[yyn]; - if (0 < yyn) - break; - } - } - - /* Pop the current state because it cannot handle the error token. */ - if (yyssp == yyss) - YYABORT; - - yydestruct("Error: popping", YY_ACCESSING_SYMBOL(yystate), yyvsp); - YYPOPSTACK(1); - yystate = *yyssp; - YY_STACK_PRINT(yyss, yyssp); - } - - YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN - *++yyvsp = yylval; - YY_IGNORE_MAYBE_UNINITIALIZED_END - - /* Shift the error token. */ - YY_SYMBOL_PRINT("Shifting", YY_ACCESSING_SYMBOL(yyn), yyvsp, yylsp); - - yystate = yyn; - goto yynewstate; - -/*-------------------------------------. -| yyacceptlab -- YYACCEPT comes here. | -`-------------------------------------*/ -yyacceptlab: - yyresult = 0; - goto yyreturnlab; - -/*-----------------------------------. -| yyabortlab -- YYABORT comes here. | -`-----------------------------------*/ -yyabortlab: - yyresult = 1; - goto yyreturnlab; - -/*-----------------------------------------------------------. -| yyexhaustedlab -- YYNOMEM (memory exhaustion) comes here. | -`-----------------------------------------------------------*/ -yyexhaustedlab: - yyerror(YY_("memory exhausted")); - yyresult = 2; - goto yyreturnlab; - -/*----------------------------------------------------------. -| yyreturnlab -- parsing is finished, clean up and return. | -`----------------------------------------------------------*/ -yyreturnlab: - if (yychar != YYEMPTY) { - /* Make sure we have latest lookahead translation. See comments at - user semantic actions for why this is necessary. */ - yytoken = YYTRANSLATE(yychar); - yydestruct("Cleanup: discarding lookahead", yytoken, &yylval); - } - /* Do not reclaim the symbols of the rule whose action triggered - this YYABORT or YYACCEPT. */ - YYPOPSTACK(yylen); - YY_STACK_PRINT(yyss, yyssp); - while (yyssp != yyss) { - yydestruct("Cleanup: popping", YY_ACCESSING_SYMBOL(+*yyssp), yyvsp); - YYPOPSTACK(1); - } -#ifndef yyoverflow - if (yyss != yyssa) - YYSTACK_FREE(yyss); -#endif - - return yyresult; -} - -#line 225 "DYacc.y" diff --git a/src/xmutil/third_party/xmutil/Dynamo/DYacc.tab.hpp b/src/xmutil/third_party/xmutil/Dynamo/DYacc.tab.hpp deleted file mode 100644 index 3a5f63f82..000000000 --- a/src/xmutil/third_party/xmutil/Dynamo/DYacc.tab.hpp +++ /dev/null @@ -1,92 +0,0 @@ -/* A Bison parser, made by GNU Bison 3.8.2. */ - -/* Bison interface for Yacc-like parsers in C - - Copyright (C) 1984, 1989-1990, 2000-2015, 2018-2021 Free Software Foundation, - Inc. - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . */ - -/* As a special exception, you may create a larger work that contains - part or all of the Bison parser skeleton and distribute that work - under terms of your choice, so long as that work isn't itself a - parser generator using the skeleton or a modified version thereof - as a parser skeleton. Alternatively, if you modify or redistribute - the parser skeleton itself, you may (at your option) remove this - special exception, which will cause the skeleton and the resulting - Bison output files to be licensed under the GNU General Public - License without this special exception. - - This special exception was added by the Free Software Foundation in - version 2.2 of Bison. */ - -/* DO NOT RELY ON FEATURES THAT ARE NOT DOCUMENTED in the manual, - especially those whose name start with YY_ or yy_. They are - private implementation details that can be changed or removed. */ - -#ifndef YY_DPYY_DYACC_TAB_HPP_INCLUDED -# define YY_DPYY_DYACC_TAB_HPP_INCLUDED -/* Debug traces. */ -#ifndef YYDEBUG -# define YYDEBUG 0 -#endif -#if YYDEBUG -extern int dpyydebug; -#endif - -/* Token kinds. */ -#ifndef YYTOKENTYPE -# define YYTOKENTYPE - enum yytokentype - { - YYEMPTY = -2, - YYEOF = 0, /* "end of file" */ - YYerror = 256, /* error */ - YYUNDEF = 257, /* "invalid token" */ - DPTT_aux = 258, /* DPTT_aux */ - DPTT_table = 259, /* DPTT_table */ - DPTT_level = 260, /* DPTT_level */ - DPTT_init = 261, /* DPTT_init */ - DPTT_constant = 262, /* DPTT_constant */ - DPTT_eoq = 263, /* DPTT_eoq */ - DPTT_groupstar = 264, /* DPTT_groupstar */ - DPTT_specs = 265, /* DPTT_specs */ - DPTT_save = 266, /* DPTT_save */ - DPTT_and = 267, /* DPTT_and */ - DPTT_macro = 268, /* DPTT_macro */ - DPTT_end_of_macro = 269, /* DPTT_end_of_macro */ - DPTT_or = 270, /* DPTT_or */ - DPTT_not = 271, /* DPTT_not */ - DPTT_ge = 272, /* DPTT_ge */ - DPTT_le = 273, /* DPTT_le */ - DPTT_ne = 274, /* DPTT_ne */ - DPTT_number = 275, /* DPTT_number */ - DPTT_symbol = 276, /* DPTT_symbol */ - DPTT_units_symbol = 277, /* DPTT_units_symbol */ - DPTT_function = 278, /* DPTT_function */ - DPTT_dt_to_one = 279 /* DPTT_dt_to_one */ - }; - typedef enum yytokentype yytoken_kind_t; -#endif - -/* Value type. */ - - -extern YYSTYPE dpyylval; - - -int dpyyparse (void); - - -#endif /* !YY_DPYY_DYACC_TAB_HPP_INCLUDED */ diff --git a/src/xmutil/third_party/xmutil/Dynamo/DYacc.y b/src/xmutil/third_party/xmutil/Dynamo/DYacc.y deleted file mode 100644 index 4d4c764e6..000000000 --- a/src/xmutil/third_party/xmutil/Dynamo/DYacc.y +++ /dev/null @@ -1,225 +0,0 @@ -/* -c:\tools\bison\bin\win_bison -o DYacc.tab.cpp -p dpyy -d DYacc.y - - -c:\tools\bison\bin\win_bison -o $(ProjectDir)src\Dynamo\DYacc.tab.cpp -p dpyy -d $(ProjectDir)src\Dynamo\DYacc.y -Converting DYacc.y -Outputs: DYacc.tab.cpp DYacc.tab.hpp -*/ - -%{ -#include "../Log.h" -#include "../Symbol/Parse.h" -#include "DynamoParseFunctions.h" -extern int dpyylex (void); -extern void dpyyerror (char const *); -#define YYSTYPE ParseUnion -#define YYFPRINTF fprintf -%} - -/* tokens returned by the tokenizer (in addition to single char tokens) */ - -/* equation definition tokens */ -%token DPTT_aux -%token DPTT_table -%token DPTT_level -%token DPTT_init -%token DPTT_constant -/* end of equation depends on the source type - in traditionally dynamo it is any white space */ -%token DPTT_eoq -/* this is actually a * but context dependent */ -%token DPTT_groupstar - -%token DPTT_specs -%token DPTT_save - -%token DPTT_and -%token DPTT_macro -%token DPTT_end_of_macro -%token DPTT_or -%token DPTT_not -%token DPTT_ge -%token DPTT_le -%token DPTT_ne - - -/* tokens with types returned by the tokenizer - for these the tokenizer needs to set yylval.var - and so on before returning the token ID */ -%token DPTT_number -%token DPTT_symbol -%token DPTT_units_symbol -%token DPTT_function -%token DPTT_dt_to_one // special for level equations - - -/* types for the different productions - note that there are quite a few - of these and that reductions in addition to creating or reusing an object - also delete the component object to cleaning up is done as the - parsing progresses */ -%type exp -%type tablepairs tablevals xytablevals xytablevec tabledef -%type exprlist -%type lhs -%type eqn -%type stock_eqn -%type teqn -%type fulleq -%type var -%type symlist sublist subdef maplist -%type number - - -/* precedence - low to high */ -%left '-' '+' -%left DPTT_or -%left '=' '<' '>' DPTT_le DPTT_ge DPTT_ne -%left DPTT_and -%left '*' '/' -%left DPTT_not -%right '^' /* exponentiation */ - -%% /* The grammar follows. */ - - -fulleq : - DPTT_eoq { return DPTT_eoq;} // end of file - | DPTT_groupstar { return DPTT_groupstar; } // depth and name follow - manage that outside - | DPTT_specs { return DPTT_specs; } - | DPTT_save { return DPTT_save; } - | DPTT_table teqn DPTT_eoq { dpyy_addfulleq($2,DPTT_table) ; return DPTT_eoq;} - | DPTT_constant eqn DPTT_eoq { dpyy_addfulleq($2,DPTT_constant) ; return DPTT_eoq; } - | DPTT_init eqn DPTT_eoq { dpyy_addfulleq($2,DPTT_init) ; return DPTT_eoq;} - | DPTT_level stock_eqn DPTT_eoq { dpyy_addfulleq($2,DPTT_level) ; return DPTT_eoq;} - | DPTT_aux eqn DPTT_eoq { dpyy_addfulleq($2,DPTT_aux) ; return DPTT_eoq;} - ; - -teqn : - lhs '=' tabledef {$$ = dpyy_add_lookup($1,NULL,$3,0) ; } - ; - - -tabledef : - number { $$ = dpyy_tablevec(NULL,$1) ;} - | tabledef ',' number { $$ = dpyy_tablevec($1,$3) ;} - | tabledef '/' number { $$ = dpyy_tablevec($1,$3) ;} - ; - - - - -eqn : - lhs '=' exprlist {$$ = dpyy_addeq($1,NULL,$3,'=') ; } - | lhs '(' tablevals ')' { $$ = dpyy_add_lookup($1,NULL,$3, 0) ; } - | lhs '(' xytablevals ')' { $$ = dpyy_add_lookup($1,NULL,$3, 1) ; } - | lhs { $$ = dpyy_add_lookup($1,NULL,NULL, 0) ; } // treat as if a lookup on time - don't have numbers - | DPTT_symbol ':' subdef maplist {$$ = dpyy_addeq(dpyy_addexceptinterp(dpyy_var_expression($1,NULL),NULL,0),(Expression *)dpyy_symlist_expression($3,$4),NULL,':') ; } - ; - -stock_eqn : - lhs '=' var '+' exprlist {$$ = dpyy_addstockeq($1,$3,$5,'=') ; } - ; - - - - -lhs : - var { $$ = dpyy_addexceptinterp($1,NULL,0) ; } - ; - -var : - DPTT_symbol { $$ = dpyy_var_expression($1,NULL);} - | DPTT_symbol sublist { $$ = dpyy_var_expression($1,$2) ;} - ; - -sublist : - '[' symlist ']' {$$ = $2 ;} - ; - -symlist : - DPTT_symbol { $$ = dpyy_symlist(NULL,$1,0,NULL) ; } - | DPTT_symbol '!' { $$ = dpyy_symlist(NULL,$1,1,NULL) ; } - | symlist ',' DPTT_symbol { $$ = dpyy_symlist($1,$3,0,NULL) ;} - | symlist ',' DPTT_symbol '!' { $$ = dpyy_symlist($1,$3,1,NULL) ;} - ; -subdef : - DPTT_symbol { $$ = dpyy_symlist(NULL,$1,0,NULL) ; } - | '(' DPTT_symbol '-' DPTT_symbol ')' {$$ = dpyy_symlist(NULL,$2,0,$4) ;} - | subdef ',' DPTT_symbol { $$ = dpyy_symlist($1,$3,0,NULL) ; } - | subdef ',' '(' DPTT_symbol '-' DPTT_symbol ')' {$$ = dpyy_symlist($1,$4,0,$6) ; } - ; - -number : - DPTT_number {$$ = $1 ; } - | '-' DPTT_number {$$ = -$2 ;} - | '+' DPTT_number {$$ = $2 ;} - ; - - -maplist : - { $$ = NULL ; } - ; - - // number lists can use ; to end a line -exprlist : - exp {$$ = dpyy_chain_exprlist(NULL,$1) ;} - | exprlist ',' exp {$$ = dpyy_chain_exprlist($1,$3) ; } - | exprlist ';' exp {$$ = dpyy_chain_exprlist($1,$3) ; } - | exprlist ';' {$$ = $1 ; } - ; - -exp: - DPTT_number { $$ = dpyy_num_expression($1) ; } /* since we allow unary - number not used here */ - | var { $$ = (Expression *)$1 ; } /* ExpressionVariable is subclassed from Expression */ - | var '(' exprlist ')' { $$ = dpyy_lookup_expression($1,$3) ; } - | '(' exp ')' { $$ = dpyy_operator_expression('(',$2,NULL) ; } - | DPTT_function '(' exprlist ')' { $$ = dpyy_function_expression($1,$3) ;} - | DPTT_function '(' exprlist ',' ')' { $$ = dpyy_function_expression($1,dpyy_chain_exprlist($3,dpyy_literal_expression("?"))) ;} - | DPTT_function '(' ')' { $$ = dpyy_function_expression($1,NULL) ;} - | exp '+' exp { $$ = dpyy_operator_expression('+',$1,$3) ; } - | exp '-' exp { $$ = dpyy_operator_expression('-',$1,$3) ; } - | exp '*' exp { $$ = dpyy_operator_expression('*',$1,$3) ; } - | exp '/' exp { $$ = dpyy_operator_expression('/',$1,$3) ; } - | exp '<' exp { $$ = dpyy_operator_expression('<',$1,$3) ; } - | exp DPTT_le exp { $$ = dpyy_operator_expression(DPTT_le,$1,$3) ; } - | exp '>' exp { $$ = dpyy_operator_expression('>',$1,$3) ; } - | exp DPTT_ge exp { $$ = dpyy_operator_expression(DPTT_ge,$1,$3) ; } - | exp DPTT_ne exp { $$ = dpyy_operator_expression(DPTT_ne,$1,$3) ; } - | exp DPTT_or exp { $$ = dpyy_operator_expression(DPTT_or,$1,$3) ; } - | exp DPTT_and exp { $$ = dpyy_operator_expression(DPTT_and,$1,$3) ; } - | DPTT_not exp { $$ = dpyy_operator_expression(DPTT_not,$2,NULL) ; } - | exp '=' exp { $$ = dpyy_operator_expression('=',$1,$3) ; } - | '-' exp { $$ = dpyy_operator_expression('-',NULL, $2) ; } /* unary plus - might be used by numbers */ - | '+' exp { $$ = dpyy_operator_expression('+',NULL, $2) ; } /* unary plus - might be used by numbers */ - | exp '^' exp { $$ = dpyy_operator_expression('^',$1,$3) ; } - ; - -tablevals : - tablepairs { $$ = $1 ; } - | '[' '(' number ',' number ')' '-' '(' number ',' number ')' ']' ',' tablepairs - { $$ = dpyy_tablerange($15,$3,$5,$9,$11) ; } - | '[' '(' number ',' number ')' '-' '(' number ',' number ')' ',' tablepairs ']' ',' tablepairs - { $$ = dpyy_tablerange($17,$3,$5,$9,$11) ; } - ; - - xytablevals : - xytablevec { $$ = $1 ; } - | '[' '(' number ',' number ')' '-' '(' number ',' number ')' ']' ',' xytablevec - { $$ = dpyy_tablerange($15,$3,$5,$9,$11) ; } - ; - - xytablevec : - number { $$ = dpyy_tablevec(NULL,$1) ;} - | xytablevec ',' number {$$ = dpyy_tablevec($1,$3) ;} - ; - - -tablepairs : - '(' number ',' number ')' { $$ = dpyy_tablepair(NULL,$2,$4) ;} - | tablepairs ',' '(' number ',' number ')' {$$ = dpyy_tablepair($1,$4,$6) ;} - ; - - - -/* End of grammar. */ -%% diff --git a/src/xmutil/third_party/xmutil/Dynamo/DynamoFunction.cpp b/src/xmutil/third_party/xmutil/Dynamo/DynamoFunction.cpp deleted file mode 100644 index b7d16b3e2..000000000 --- a/src/xmutil/third_party/xmutil/Dynamo/DynamoFunction.cpp +++ /dev/null @@ -1,117 +0,0 @@ -#include "DynamoFunction.h" - -#include "../Symbol/ExpressionList.h" -#include "../XMUtil.h" - -// dynamo uses table y values as a vector and then x,min,max,increment so we output x and then setup the graphical based -// on y vals -/* - - - 13.1,46.1,48.5,49.5,51,52.9,53.9,55.3,55.3,55.3,55.3,54.4,0 - -*/ -void DFunctionTable::OutputComputable(ContextInfo *info, ExpressionList *arg) { - if (arg->Length() == 5) { - *info << "LOOKUP("; - const_cast((*arg)[0])->OutputComputable(info); - *info << ", "; - const_cast((*arg)[1])->OutputComputable(info); - *info << ")"; - } else { - *info << "{error untranslatable table function call}"; - Function::OutputComputable(info, arg); - } -} - -bool DFunctionTable::SetTableXAxis(ExpressionList *args) const { - if (args->Length() == 5) { - ExpressionVariable *evar = dynamic_cast(const_cast((*args)[0])); - if (evar) { - double xmin = 0; - double xmax = 1; - double increment = 1; - Variable *var = evar->GetVariable(); - Expression *exp = const_cast((*args)[2]); - if (exp->GetType() == EXPTYPE_Number) - xmin = exp->Eval(NULL); - exp = const_cast((*args)[3]); - if (exp->GetType() == EXPTYPE_Number) - xmax = exp->Eval(NULL); - exp = const_cast((*args)[4]); - if (exp->GetType() == EXPTYPE_Number) - increment = exp->Eval(NULL); - std::vector equations = var->GetAllEquations(); - size_t n = equations.size(); - // todo arrayed differences???? - bool found = false; - for (size_t i = 0; i < n; i++) { - Equation *eq = equations[i]; - exp = eq->GetExpression(); - if (exp->GetType() == EXPTYPE_Table) { - static_cast(exp)->SetXAxis(var, xmin, xmax, increment); - ; - found = true; - } - } - if (!found) - log("ERROR - %s is used as a table but not defined that way.\n", var->GetName().c_str()); - return found; - } - } - return false; -} -void DFunctionPulse::OutputComputable(ContextInfo *info, ExpressionList *arg) { - if (arg->Length() == 2) { - *info << "( IF TIME >= ("; - const_cast((*arg)[0])->OutputComputable(info); // OutputComputable should really be const - *info << ") AND TIME < (("; - const_cast((*arg)[0])->OutputComputable(info); // OutputComputable should really be const - *info << ") + MAX(DT,"; - const_cast((*arg)[1])->OutputComputable(info); // OutputComputable should really be const - *info << ")) THEN 1 ELSE 0 )"; - return; - } - Function::OutputComputable(info, arg); -} - -void DFunctionIfThenElse::OutputComputable(ContextInfo *info, ExpressionList *arg) { - if (arg->Length() == 3) { - *info << "( IF "; - const_cast((*arg)[0])->OutputComputable(info); // OutputComputable should really be const - *info << " THEN "; - const_cast((*arg)[1])->OutputComputable(info); // OutputComputable should really be const - *info << " ELSE "; - const_cast((*arg)[2])->OutputComputable(info); // OutputComputable should really be const - *info << " )"; - return; - } - Function::OutputComputable(info, arg); -} - -void DFunctionDelayN::OutputComputable(ContextInfo *info, ExpressionList *arg) { - if (arg->Length() == 4) { - *info << "DELAYN("; - const_cast((*arg)[0])->OutputComputable(info); - *info << ","; - const_cast((*arg)[1])->OutputComputable(info); - *info << ","; - const_cast((*arg)[3])->OutputComputable(info); - *info << ","; - const_cast((*arg)[2])->OutputComputable(info); - *info << ")"; - } -} -void DFunctionSmoothN::OutputComputable(ContextInfo *info, ExpressionList *arg) { - if (arg->Length() == 4) { - *info << "SMTHN("; - const_cast((*arg)[0])->OutputComputable(info); - *info << ","; - const_cast((*arg)[1])->OutputComputable(info); - *info << ","; - const_cast((*arg)[3])->OutputComputable(info); - *info << ","; - const_cast((*arg)[2])->OutputComputable(info); - *info << ")"; - } -} diff --git a/src/xmutil/third_party/xmutil/Dynamo/DynamoFunction.h b/src/xmutil/third_party/xmutil/Dynamo/DynamoFunction.h deleted file mode 100644 index 8fe164957..000000000 --- a/src/xmutil/third_party/xmutil/Dynamo/DynamoFunction.h +++ /dev/null @@ -1,128 +0,0 @@ -#ifndef _XMUTIL_DYNAMO_FUNCTION_H -#define _XMUTIL_DYNAMO_FUNCTION_H -/* function declarations for dynamo - should also probably split off the Vensim ones*/ -#include "../Function/Function.h" - -class DFunctionTable : public Function { -public: - DFunctionTable(SymbolNameSpace *sns) : Function(sns, "TABLE", 5) { - } - ~DFunctionTable(void) { - } - virtual void OutputComputable(ContextInfo *info, ExpressionList *arg) override; - virtual bool IsTableCall() const override { - return true; - } - bool SetTableXAxis(ExpressionList *args) const; - -private: -}; - -FSubclassMemoryStart(DFunctionInteg, "INTEGRATE", 2, 0b10, 0b01, "integ_active", - "integ_init") virtual bool IsIntegrator() const override { - return true; -} -} -; - -FSubclass(DFunctionAbs, "ABS", 1, "ABS"); -FSubclass(DFunctionExp, "EXP", 1, "EXP"); -FSubclass(DFunctionSqrt, "SQRT", 1, "SQRT"); - -FSubclass(DFunctionCosine, "COS", 1, "COS"); -FSubclass(DFunctionTangent, "TAN", 1, "TAN"); -FSubclass(DFunctionSine, "SIN", 1, "SIN"); -FSubclass(DFunctionArcCosine, "ARCCOS", 1, "ARCCOS"); -FSubclass(DFunctionArcSine, "ARCSIN", 1, "ARCSIN"); -FSubclass(DFunctionArcTangent, "ARCTAN", 1, "ARCTAN"); -FSubclass(DFunctionInterger, "INTEGER", 1, "INT"); - -FSubclass(DFunctionMax, "MAX", 2, "MAX"); -FSubclass(DFunctionMin, "MIN", 2, "MIN"); -FSubclass(DFunctionZidz, "FIFZE", 2, "SAFEDIV"); - -FSubclass(DFunctionXidz, "XIDZ", 3, "SAFEDIV"); - -FSubclass(DFunctionSum, "SUM", 1, "SUM"); -FSubclass(DFunctionProd, "PROD", 1, "PROD"); -FSubclass(DFunctionVMax, "VMAX", 1, "MAX"); -FSubclass(DFunctionVMin, "VMIN", 1, "MIN"); - -FSubclass(DFunctionRandom01, "RANDOM 0 1", 0, "UNIFORM(0,1)"); -FSubclass(DFunctionRandomUniform, "RANDOM UNIFORM", 3, "UNIFORM"); -FSubclass(DFunctionRandomPink, "RANDOM PINK NOISE", 4, "NORMALPINK"); - -// actually memory but no init - or init - does not matter for translation -DFSubclass(DFunctionSmooth, "SMOOTH", 2, "SMTH1"); -DFSubclass(DFunctionSmoothI, "SMOOTHI", 3, "SMTH1"); -DFSubclass(DFunctionSmooth3, "SMOOTH3", 2, "SMTH3"); -DFSubclass(DFunctionSmooth3I, "SMOOTH3I", 3, "SMTH3"); -DFSubclass(DFunctionTrend, "TREND", 3, "TREND"); -DFSubclass(DFunctionFrcst, "FORECAST", 3, "FORCST"); -DFSubclass(DFunctionDelay1, "DELAY1", 2, "DELAY1"); -DFSubclass(DFunctionDelay1I, "DELAY1I", 3, "DELAY1"); -DFSubclass(DFunctionDelay3, "DELAY3", 2, "DELAY3"); -DFSubclass(DFunctionDelay3I, "DELAY3I", 3, "DELAY3"); -DFSubclass(DFunctionDelay, "DELAY FIXED", 3, "DELAY"); -DFSubclass(DFunctionNPV, "NPV", 4, "NPV"); - -FSubclass(DFunctionModulo, "MODULO", 2, "MODULO"); - -FSubclassStart(DFunctionDelayN, "DELAY N", 4, "DELAYN") public - : virtual void OutputComputable(ContextInfo *info, ExpressionList *arg) override; -} -; -FSubclassStart(DFunctionSmoothN, "SMOOTH N", 4, "SMTHN") public - : virtual void OutputComputable(ContextInfo *info, ExpressionList *arg) override; -} -; - -FSubclassMemoryStart(DFunctionActiveInitial, "ACTIVE INITIAL", 2, 0b10, 0b01, "ai_active", - "ai_init") virtual bool IsActiveInit() override { - return true; -} -} -; -FSubclass(DFunctionInitial, "INITIAL", 1, "INIT"); -FSubclass(DFunctionReInitial, "REINITIAL", 1, "INIT"); - -FSubclassTime(DFunctionRamp, "RAMP", 3, "RAMP"); -FSubclass(DFunctionLn, "LN", 1, "LN"); -FSubclassTime(DFunctionStep, "STEP", 2, "step"); - -FSubclassKeyword(DFunctionTabbedArray, "TABBED ARRAY", 1); - -// functions that will never translate - but easier to catch error on other side -FSubclass(DFunctionGetDirectData, "GET DIRECT DATA", 4, "GET_DIRECT_DATA"); -FSubclass(DFunctionGetDataMean, "GET DATA MEAN", 3, "GET_DATA_MEAN"); - -class DFunctionPulse : public Function { -public: - DFunctionPulse(SymbolNameSpace *sns) : Function(sns, "PULSE", 2) { - } - ~DFunctionPulse(void) { - } - std::string ComputableName(void) override { - return "Pulse"; - } - virtual void OutputComputable(ContextInfo *info, ExpressionList *arg) override; - -private: -}; - -class DFunctionIfThenElse : public Function { -public: - DFunctionIfThenElse(SymbolNameSpace *sns) : Function(sns, "IF THEN ELSE", 3) { - ; - } - ~DFunctionIfThenElse(void) { - } - std::string ComputableName(void) override { - return "IF"; - } - virtual void OutputComputable(ContextInfo *info, ExpressionList *arg) override; - -private: -}; - -#endif diff --git a/src/xmutil/third_party/xmutil/Dynamo/DynamoLex.cpp b/src/xmutil/third_party/xmutil/Dynamo/DynamoLex.cpp deleted file mode 100644 index bd8b28010..000000000 --- a/src/xmutil/third_party/xmutil/Dynamo/DynamoLex.cpp +++ /dev/null @@ -1,695 +0,0 @@ -/* DynamoLex - - */ -#include "DynamoLex.h" - -#include "DynamoParse.h" -/* try to avoid the tab.h file as it is C */ -#define YYSTYPE ParseUnion -#include "../Model.h" -#include "../Symbol/Expression.h" -#include "../Symbol/Variable.h" -#include "../XMUtil.h" -#include "DYacc.tab.hpp" - -DynamoLex::DynamoLex(void) { - ucContent = NULL; - iCurPos = iFileLength = 0; - bInEquation = false; - bNoSpace = true; - bClassicParsing = true; - GetReady(); -} -DynamoLex::~DynamoLex() { -} -void DynamoLex::Initialize(const char *content, off_t length) { - ucContent = content; - iFileLength = length; - iLineStart = iCurPos = 0; - iLineNumber = 1; - GetReady(); - unsigned char c = GetNextChar(false); - if (c == 0xEF) { - c = GetNextChar(false); - if (c == 0xBB) { - c = GetNextChar(false); - if (c != 0xBF) { - if (c) - this->PushBack(c, false); - this->PushBack(0xBF, false); - this->PushBack(0xBB, false); - } - } else { - if (c) - this->PushBack(c, false); - this->PushBack(0xBB, false); - } - } else if (c) - this->PushBack(c, false); -} -int DynamoLex::GetEndToken(void) { - return DPTT_eoq; -} - -void DynamoLex::GetReady(void) { - iInUnitsComment = 0; - bInUnits = false; -} -std::string *DynamoLex::CurToken() { - return &sToken; -} - -int DynamoLex::yylex() { - int toktype = NextToken(); - // printf("Token type %d %s\n", toktype, sToken.c_str()); - switch (toktype) { - case DPTT_number: - dpyylval.num = atof(sToken.c_str()); - break; - case DPTT_symbol: - if (bInUnits) { - Units *units = DPObject->InsertUnits(sToken); - if (!units) { - // InsertUnits failed - return end token to stop parsing - return DPTT_eoq; - } - dpyylval.uni = DPObject->InsertUnitExpression(units); - toktype = DPTT_units_symbol; - break; - } - // special things here - try to do almost everything (including INTEG) as a function but some need to call out to - // different toktypes - dpyylval.sym = DPObject->InsertVariable(sToken); - if (!dpyylval.sym) { - // InsertVariable failed - return end token to stop parsing - return DPTT_eoq; - } - if (dpyylval.sym->isType() == Symtype_Function) { - toktype = DPTT_function; - } - - break; - case 0: - toktype = DPTT_eoq; - // Fall through to also clear bInEquation flag when we hit end of input - [[fallthrough]]; - case DPTT_eoq: - bInEquation = false; - break; - default: - break; - } - return toktype; -} - -void DynamoLex::GetDigits() { - char c; - while (1) { - c = GetNextChar(true); - if (c < '0' || c > '9') { - PushBack(c, true); - break; - } - } -} - -int DynamoLex::NextToken() // also sets token type -{ - unsigned char c; - int toktype; - - while (true) { - c = GetNextChar(false); - if (c == '\n' || c == '\r' || c == '\f') - bNoSpace = true; - else if (c == ' ' || c == '\t') - bNoSpace = false; // consume whitespace - else - break; - if (bInEquation && bClassicParsing) - return DPTT_eoq; - } - if (!c) - return 0; - sToken.clear(); - PushBack(c, false); - c = GetNextChar(true); - - toktype = c; // default for many tokens - switch (c) { - case '*': // check for *** which names groups - we are dropping group info for now - if (!bInEquation) { - sToken.pop_back(); - assert(sToken.empty()); - while (true) { - c = GetNextChar(true); - if (c != '*') - break; - } - if (c) - PushBack(c, true); - do { - c = GetNextChar(false); - } while (c == ' ' || c == '\t'); // consume whitespace - if (c) - PushBack(c, false); - // the treat everything till end of line as the group name - it is is empty call it so - do { - c = GetNextChar(true); - } while (c != '\n' && c != '\r'); // consume whitespace - if (c) - PushBack(c, true); - return DPTT_groupstar; - } - break; - // single character tokens - case '~': - if (iInUnitsComment == 0) - bInUnits = true; - iInUnitsComment++; - break; - case '=': // := is handled by : - if (TestTokenMatch("=", true)) - return '='; // we ignore the invariant == that Dynamo supports - break; - case '/': - break; - case '^': - break; - case '!': - break; - case '(': - break; - case ')': - break; - case '}': - break; - case '[': - if (iInUnitsComment) - bInUnits = false; - case ']': - break; - case '|': - break; - case ',': - break; - case '+': - break; - case '>': - if (TestTokenMatch("=", true)) - return DPTT_ge; - break; - case '-': - break; - case '<': - if (TestTokenMatch("=", true)) - return DPTT_le; - if (TestTokenMatch(">", true)) - return DPTT_ne; - break; - case '1': - if (bInUnits) { - Units *units = DPObject->InsertUnits("1"); - if (!units) { - // InsertUnits failed - return end token to stop parsing - return DPTT_eoq; - } - dpyylval.uni = DPObject->InsertUnitExpression(units); - return DPTT_units_symbol; - } - /* fallthrough */ - case '.': // maybe a number check next digit - case '0': - - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - case '8': - case '9': - // a number follow it through till it is no longer a number - if (c == '.') { - c = GetNextChar(true); - if (c < '0' || c > '9') { - PushBack(c, true); - break; // not a number return '.' - } - GetDigits(); - } else { - GetDigits(); - c = GetNextChar(true); - if (c != '.') - PushBack(c, true); - } - toktype = DPTT_number; - if (c == '.') - GetDigits(); - c = GetNextChar(true); - if (c == 'E' || c == 'e') { // xxx.xxxE+-xx - c = GetNextChar(true); - if (c != '+' && c != '-') - PushBack(c, true); - GetDigits(); - } else - PushBack(c, true); - break; - case ':': // := :AND: :HOLD BACKWARD: :IMPLIES: :INTERPOLATE: :LOOK FORWARD: :OR: :NA: :NOT: :RAW: :TEST INPUT: - // :THE CONDITION: - break; - case '{': // a comment, find matching } - { // nesting, len local in scope - int nesting = 1; - int len = 1; - MarkPosition(); - while ((c = GetNextChar(false))) { - len++; - if (len > 1028) - break; // excessive comments not considered valid - if (c == '}') { - nesting--; - if (!nesting) { // comment is in sToken right now - return NextToken(); // just skip the comment for now - as if not there - } - } else if (c == '{') - nesting++; - else if (c == '*' && nesting == 1) { - c = GetNextChar(false); - if (c == '*') // treat this as a group - from the original dynamo format - { - while ((c = GetNextChar(false)) == '*') - ; - while (c == '\r' || c == '\n' || c == ' ' || c == '\t') - c = GetNextChar(false); - if (c == '}') - return NextToken(); // no useful group name - sToken.clear(); - do { - this->sToken.push_back(c); - if (c == '.') { - sToken.pop_back(); - if (!sToken.empty()) - sToken.push_back('-'); // can't use . in a module name - } - c = GetNextChar(false); - } while (c != '\r' && c != '\n' && c != '*' && c != '}'); - while (sToken.back() == ' ') - sToken.pop_back(); - while (c && c != '}') - c = GetNextChar(false); - return DPTT_groupstar; - } else - PushBack(c, false); - } - } - ReturnToMark(); // failed to find pair give up - } break; // give up and just return the one char - will throw error message - case '\'': - break; - case '\"': // a quoted variable name potentially with embedded escaped quotes - { - int len; - MarkPosition(); - for (len = 1; (c = GetNextChar(true)); len++) { - if (c == '\"') { - return DPTT_symbol; // the returned token includes both the opening and closing quote - } else if (c == '\\') { // skip what follows in case it is a " or \ -- - GetNextChar(true); - len++; - } - if (len > 1024) // apparently unmmatched - break; - } - } - ReturnToMark(); - break; // give up and just return the one char - case '\\': // - break; - default: // a variable name or an unrecognizable token - if (!bInEquation && bNoSpace) { - int rtype = 0; - switch (c) { - case 'L': - case 'l': - rtype = DPTT_level; - break; - case 'T': - case 't': - rtype = DPTT_table; - break; - case 'A': - case 'a': - rtype = DPTT_aux; - break; - case 'C': - case 'c': - rtype = DPTT_constant; - break; - case 'N': - case 'n': - rtype = DPTT_init; - break; - case 'S': - case 's': - case 'P': - case 'p': { - // SPEC/SAVE/PRINT/PLOT - char c1 = GetNextChar(true); - char c2 = GetNextChar(true); - char c3 = GetNextChar(true); - int rval = 0; - if ((c1 == 'P' || c1 == 'p') && (c2 == 'E' || c2 == 'e') && (c3 == 'C' || c3 == 'c')) - rval = DPTT_specs; - else if ((c1 == 'A' || c1 == 'a') && (c2 == 'V' || c2 == 'v') && (c3 == 'E' || c3 == 'e')) - rval = DPTT_save; - else if ((c1 == 'R' || c1 == 'r') && (c2 == 'I' || c2 == 'i') && (c3 == 'N' || c3 == 'n')) - rval = DPTT_save; - else if ((c1 == 'L' || c1 == 'l') && (c2 == 'O' || c2 == 'o') && (c3 == 'T' || c3 == 't')) - rval = DPTT_save; - if (rval) { - do { - c = GetNextChar(false); - } while (c == ' ' || c == '\t'); - PushBack(c, false); - return rval; - } - PushBack(c3, true); - PushBack(c2, true); - PushBack(c1, true); - PushBack(c, true); - } - } - if (rtype) { - bInEquation = true; - // and get rid of any blank space before the real start of the equation - do { - c = GetNextChar(false); - } while (c == ' ' || c == '\t' || c == '\r' || c == '\n'); - if (c) - PushBack(c, false); - return rtype; - } - } - - if (isalpha(c) || c > 127 || ((iInUnitsComment == 1) && c == '$')) { // a variable - while ((c = GetNextChar(true))) { - if (bClassicParsing && bInEquation && (c == ' ' || c == '\t')) { - PushBack(c, true); - break; - } - if (!isalnum(c) && c != ' ' && c != '_' && c != '$' && c != '\t' && c != '\'' && c < 128) { - PushBack(c, true); - if (c == '.') { - // we alloc .j .k .jk and .kl everywhere by simply stripping them - c = GetNextChar(false); - c = GetNextChar(false); - if (c == 'j' || c == 'J' || c == 'k' || c == 'K') { - unsigned char c2 = GetNextChar(false); - if (c2 != 'j' && c2 != 'J' && c2 != 'k' && c2 != 'K') - PushBack(c2, false); - } else { - PushBack(c, false); - PushBack('.', false); - } - } - break; - } - } - // strip any terminal spaces - while (sToken.back() == ' ' || sToken.back() == '_') - sToken.pop_back(); - return DPTT_symbol; - } - } - return toktype; -} - -// target assumed upper case -bool DynamoLex::KeywordMatch(const char *target) { - std::string buffer; - char c; - int i; - for (i = 0; target[i]; i++) { - c = GetNextChar(true); - if (target[i] == ' ') { /* one or more _ or space ok here */ - if (c != ' ' && c != '_' && c != '\t') - break; - buffer.push_back(c); - while ((c = GetNextChar(true))) { - if (c != ' ' && c != '_' && c != '\t') - break; - buffer.push_back(c); - } - PushBack(c, true); - } else if (toupper(c) != target[i]) - break; - else - buffer.push_back(c); - } - if (target[i]) { // not a match - PushBack(c, true); // last one taken should be sent back - while (buffer.length()) { - PushBack(buffer.back(), true); - buffer.pop_back(); - } - return false; - } - return true; -} - -char DynamoLex::GetNextChar(bool store) { - char c; - if (sBuffer.length()) { - c = sBuffer.back(); - sBuffer.pop_back(); - if (store) - sToken.push_back(c); - return c; - } - if (iCurPos >= iFileLength) - return 0; // nothing to do - c = ucContent[iCurPos++]; - if (c == '\\') { // check for continuation lines - if (iCurPos < iFileLength && (ucContent[iCurPos] == '\n' || ucContent[iCurPos] == '\r')) { - for (; iCurPos < iFileLength;) { - c = ucContent[iCurPos++]; - if (c == '\n') { - iLineNumber++; - iLineStart = iCurPos + 1; // actually the next pos - } else if (c != '\t' && c != ' ' && c != '\r') - break; - // note as in vensim two \ line ends in a row just cause an error - } - } - } else if (c == '\n') { - iLineNumber++; - iLineStart = iCurPos; // actually the next pos - } - if (store) - sToken.push_back(c); - return c; -} - -// check for token match - advance position on success -bool DynamoLex::TestTokenMatch(const char *tok, bool storeonsuccess) { - char c; - if (!*tok) - return true; - int i; - for (i = 0; tok[i]; i++) { - if ((c = GetNextChar(storeonsuccess)) != tok[i]) - break; - } - if (tok[i]) { - PushBack(c, storeonsuccess); - while (i-- > 0) - PushBack(tok[i], storeonsuccess); - return false; - } - return true; -} - -static void trim_ends(std::string &comment) { - size_t i; - for (i = 0; i < comment.length(); i++) { - char c = comment[i]; - if (c != ' ' && c != '\t' && c != '\r' && c != '\n') - break; - } - int start = i; - for (i = comment.length(); i-- > 0;) { - char c = comment[i]; - if (c != ' ' && c != '\t' && c != '\r' && c != '\n') - break; - } - comment = comment.substr(start, i + 1 - start); -} - -// todo figure out units -std::string DynamoLex::GetComment(std::string &units) { - char c; - std::string comment; - bNoSpace = false; - while (true) { - c = GetNextChar(false); - if (c == '\r' || c == '\n') - bNoSpace = true; - else if (c == ' ' || c == '\t') - bNoSpace = false; - else if (bNoSpace || !c) { - if (c) - PushBack(c, false); // next call to findToken will find this - // strip trailing white space - trim_ends(comment); - if (comment.back() == ')') // maybe only do this for classic? - { - int nesting = 0; - int pos = comment.length(); - while (pos-- > 0) { - char c = comment[pos]; - if (c == ')') - nesting++; - else if (c == '(') { - nesting--; - if (nesting == 0) { - comment.pop_back(); - units = comment.substr(pos + 1); - comment = comment.substr(0, pos); - trim_ends(comment); - trim_ends(units); - } - } - } - } - return comment; - } - if (bClassicParsing) { - if (c == ' ' || c == '\t' || c == '\r' || c == '\n') { - if (comment.back() != ' ') - comment.push_back(' '); - } else - comment.push_back(c); - } else - comment.push_back(c); - } - return comment; // this is never reached -} - -bool DynamoLex::FindStartToken() { - char c; - while ((c = GetNextChar(false))) { - if (c == '\r' || c == '\n') - bNoSpace = true; - else if (c == ' ' || c == '\t') - bNoSpace = false; - else { - PushBack(c, false); - return true; - } - } - return false; -} - -bool DynamoLex::BufferReadLine(char *buf, size_t buflen) { - const char *tv = sBuffer.c_str(); - while (buflen > 0 && *tv) { - *buf = *tv; - buf++; - buflen--; - tv++; - } - return ReadLine(buf, buflen); -} -bool DynamoLex::ReadLine(char *buf, size_t buflen) { - char c; - buflen--; // need \0 - size_t off = 0; - while (iCurPos < iFileLength) { - c = ucContent[iCurPos++]; - if (off >= buflen) { - iCurPos--; - buf[off] = '\0'; - return true; - } - if (c == '\n') { - buf[off] = '\0'; - if (iCurPos < iFileLength && ucContent[iCurPos] == '\r') - iCurPos++; - return true; - } - if (c == '\r') { - buf[off] = '\0'; - if (iCurPos < iFileLength && ucContent[iCurPos] == '\n') - iCurPos++; - return true; - } - buf[off++] = c; - } - buf[0] = '\0'; - return false; -} - -void DynamoLex::PushBack(char c, bool store) { - assert(c); - sBuffer.push_back(c); - if (store) - sToken.pop_back(); -} -void DynamoLex::SyncBuffers(void) { - int i = sBuffer.length(); - iCurPos -= i; - sBuffer.clear(); -} - -void DynamoLex::ParseSpecs(Model *model) { - // expect SAVPER=2/Length=200/DT=.0078125/Rel_Err=.01 - try to get lenght and Dt - if (model == NULL) { - while (true) { - char c = GetNextChar(false); - if (c == '\r' || c == '\n') { - PushBack(c, false); - return; - } else if (!c) - return; - } - return; // not reeached - } - bInEquation = true; - bool oldclassic = bClassicParsing; - bClassicParsing = true; - // Spec SAVPER=2/Length=200/DT=.0078125/Rel_Err=.01 - int tok = DPTT_symbol; - while (tok && tok != DPTT_eoq) { - tok = NextToken(); - if (tok == DPTT_symbol) { - std::string *name = SymbolNameSpace::ToLowerSpace(sToken); - if (name) { - tok = NextToken(); - if (tok == '=') { - tok = NextToken(); - if (tok == DPTT_number) { - double val = atof(sToken.c_str()); - if (*name == "dt") - model->set_dt(val); - else if (*name == "length") { - model->set_initial_time(0); - model->set_dt(val); - } - } - delete name; - } - } - } - } - assert(bInEquation); - bInEquation = false; - bClassicParsing = oldclassic; -} - -void DynamoLex::ConsumeCurrentLine() { - char c; - do { - c = GetNextChar(false); - } while (c && c != '\r' && c != '\n'); - bInEquation = false; -} \ No newline at end of file diff --git a/src/xmutil/third_party/xmutil/Dynamo/DynamoLex.h b/src/xmutil/third_party/xmutil/Dynamo/DynamoLex.h deleted file mode 100644 index 73aeec2e9..000000000 --- a/src/xmutil/third_party/xmutil/Dynamo/DynamoLex.h +++ /dev/null @@ -1,68 +0,0 @@ -#ifndef _XMUTIL_DYNAMO_DYNAMOLEX_H -#define _XMUTIL_DYNAMO_DYNAMOLEX_H - -/* a tokenizer for Dynamo files - it is called by DynamoPare both - indirectly throught the Bison generated parser and directly for - comments and group defs */ -#include -#include - -#include "../Symbol/Parse.h" - -class Model; -class DynamoLex { -public: - DynamoLex(void); - ~DynamoLex(void); - void Initialize(const char *content, off_t length); - std::string *CurToken(void); - void GetReady(void); - int yylex(void); - int GetEndToken(void); - int LineNumber(void) { - return iLineNumber; - } - int Position(void) { - return iCurPos - iLineStart; - } - std::string GetComment(std::string &units); - bool FindStartToken(); // realistically just a line that begins with non white space - bool BufferReadLine(char *buf, size_t buflen); // start with buffer then read the line - bool ReadLine(char *buf, size_t buflen); // read a line if enough room otherwise part of it - void ParseSpecs(Model *model); - void ConsumeCurrentLine(); - -protected: - int NextToken(void); - -private: - char GetNextChar(bool store); - void PushBack(char c, bool store); - void SyncBuffers(void); - bool TestTokenMatch(const char *tok, bool update); - std::string sToken; - std::string sBuffer; - const char *ucContent; - off_t iCurPos, iHoldPos; - off_t iLineStart, iHoldStart; - void MarkPosition(void) { - iHoldPos = iCurPos; - iHoldStart = iLineStart; - } - void ReturnToMark(void) { - iCurPos = iHoldPos; - iLineStart = iHoldStart; - sBuffer.clear(); - } - int iLineNumber; - off_t iFileLength; - bool KeywordMatch(const char *target); - void GetDigits(void); - int iInUnitsComment; // 0 no, 1 units, 2 comment - bool bInUnits; - bool bInEquation; - bool bNoSpace; // since CR/LF - anything indented is treated as commentary outside of equations - bool bClassicParsing; // spaces end equations -}; - -#endif diff --git a/src/xmutil/third_party/xmutil/Dynamo/DynamoParse.cpp b/src/xmutil/third_party/xmutil/Dynamo/DynamoParse.cpp deleted file mode 100644 index c6a572ecb..000000000 --- a/src/xmutil/third_party/xmutil/Dynamo/DynamoParse.cpp +++ /dev/null @@ -1,658 +0,0 @@ -// DynamoParse.cpp : Read an mdl file into an XModel object -// we use an in-memory string to simplify look ahead/back -// we include the tokenizer here because it is as easy as setting -// up regular expressions for Flex and more easily understood - -#include "DynamoParse.h" - -#include - -#include "../Symbol/ExpressionList.h" -#include "../Symbol/LeftHandSide.h" -#include "../Symbol/Variable.h" -#include "DynamoFunction.h" -#define YYSTYPE DynamoParse -#include "../Model.h" -#include "../XMUtil.h" -#include "DYacc.tab.hpp" - -DynamoParse *DPObject = NULL; -DynamoParse::DynamoParse(Model *model) { - assert(!DPObject); - DPObject = this; - _model = model; - _model->set_from_dynamo(true); - pSymbolNameSpace = model->GetNameSpace(); - bLongName = true; - bInMacro = false; - ReadyFunctions(); -} -DynamoParse::~DynamoParse(void) { - DPObject = NULL; -} - -void DynamoParse::ReadyFunctions() { - // initialize functions - use only D functions - try { - new DFunctionTable(pSymbolNameSpace); - - new DFunctionMin(pSymbolNameSpace); - new DFunctionMax(pSymbolNameSpace); - new DFunctionInteg(pSymbolNameSpace); - new DFunctionActiveInitial(pSymbolNameSpace); - new DFunctionInitial(pSymbolNameSpace); - new DFunctionReInitial(pSymbolNameSpace); - new DFunctionPulse(pSymbolNameSpace); - new DFunctionIfThenElse(pSymbolNameSpace); - new DFunctionZidz(pSymbolNameSpace); - new DFunctionXidz(pSymbolNameSpace); - new DFunctionStep(pSymbolNameSpace); - new DFunctionTabbedArray(pSymbolNameSpace); - new DFunctionRamp(pSymbolNameSpace); - new DFunctionLn(pSymbolNameSpace); - new DFunctionSmooth(pSymbolNameSpace); - new DFunctionSmoothI(pSymbolNameSpace); - new DFunctionSmooth3(pSymbolNameSpace); - new DFunctionSmooth3I(pSymbolNameSpace); - new DFunctionTrend(pSymbolNameSpace); - new DFunctionFrcst(pSymbolNameSpace); - new DFunctionDelay1(pSymbolNameSpace); - new DFunctionDelay1I(pSymbolNameSpace); - new DFunctionDelay3(pSymbolNameSpace); - new DFunctionDelay3I(pSymbolNameSpace); - new DFunctionDelay(pSymbolNameSpace); - new DFunctionDelayN(pSymbolNameSpace); - new DFunctionSmoothN(pSymbolNameSpace); - new DFunctionModulo(pSymbolNameSpace); - new DFunctionNPV(pSymbolNameSpace); - new DFunctionSum(pSymbolNameSpace); - new DFunctionProd(pSymbolNameSpace); - new DFunctionVMax(pSymbolNameSpace); - new DFunctionVMin(pSymbolNameSpace); - new DFunctionRandom01(pSymbolNameSpace); - new DFunctionRandomUniform(pSymbolNameSpace); - new DFunctionRandomPink(pSymbolNameSpace); - new DFunctionAbs(pSymbolNameSpace); - new DFunctionExp(pSymbolNameSpace); - new DFunctionSqrt(pSymbolNameSpace); - - new DFunctionCosine(pSymbolNameSpace); - new DFunctionSine(pSymbolNameSpace); - new DFunctionTangent(pSymbolNameSpace); - new DFunctionArcCosine(pSymbolNameSpace); - new DFunctionArcSine(pSymbolNameSpace); - new DFunctionArcTangent(pSymbolNameSpace); - new DFunctionInterger(pSymbolNameSpace); - - new DFunctionGetDirectData(pSymbolNameSpace); - new DFunctionGetDataMean(pSymbolNameSpace); - - pSymbolNameSpace->ConfirmAllAllocations(); - } catch (...) { - log("Failed to initialize symbol table"); - } -} -Equation *DynamoParse::AddEq(LeftHandSide *lhs, Expression *ex, ExpressionList *exl, int tok) { - if (exl) { - if (exl->Length() == 1) { - ex = exl->GetExp(0); - delete exl; - } else { /* only a list of numbers is valid here - throw exception for anything else */ - ExpressionNumberTable *ent = new ExpressionNumberTable(pSymbolNameSpace); - int n = exl->Length(); - int i; - for (i = 0; i < n; i++) { - ex = exl->GetExp(i); - if (ex->GetType() == EXPTYPE_Operator && ex->GetArg(0) == NULL && *ex->GetOperator() == '-') { - // alloe unary minus here - ent->AddValue(0, -ex->GetArg(1)->Eval(NULL)); // note eval does not need context for number - } else if (ex->GetType() != EXPTYPE_Number) { - has_error_ = true; - last_error_ = "Expecting only comma delimited numbers "; - delete ex; // Delete the current expression that caused the error - delete ent; - delete exl; - return nullptr; - } else - ent->AddValue(0, ex->Eval(NULL)); // note eval does not need context for number - delete ex; - } - delete exl; - ex = ent; - } - } - - return new Equation(pSymbolNameSpace, lhs, ex, tok); -} -Equation *DynamoParse::AddStockEq(LeftHandSide *lhs, ExpressionVariable *stock, ExpressionList *exl, int tok) { - if (stock) { - // needs to match lhs or we throu an exception - if (lhs->GetVariable() != stock->GetVariable()) { - has_error_ = true; - last_error_ = "Level equations must be stock=stock+flow in form "; - return nullptr; - } - } - Expression *ex = NULL; - if (exl) { - if (exl->Length() == 1) { - ex = exl->GetExp(0); - delete exl; - } - } - if (ex == NULL) { - has_error_ = true; - last_error_ = "Bad level equation "; - return nullptr; - } - // wrap the expression/DT in INTEGRATE - the DT will probably just cancel another DT - so could be reduced - ex = DPObject->OperatorExpression('(', ex, NULL); - Variable *var = this->InsertVariable("DT"); - if (!var) { - delete ex; - has_error_ = true; - last_error_ = "Failed to insert DT variable"; - return nullptr; - } - Expression *ex2 = DPObject->VarExpression(var, NULL); - ex = DPObject->OperatorExpression('/', ex, ex2); - ExpressionList *args = DPObject->ChainExpressionList(NULL, ex); - var = DPObject->InsertVariable("INTEGRATE"); - if (!var) { - delete args; - has_error_ = true; - last_error_ = "Failed to insert INTEGRATE variable"; - return nullptr; - } - ex = DPObject->FunctionExpression(static_cast(static_cast(var)), args); - - return new Equation(pSymbolNameSpace, lhs, ex, DPTT_dt_to_one); -} - -Equation *DynamoParse::AddTable(LeftHandSide *lhs, Expression *ex, ExpressionTable *tbl, bool legacy) { - if (!tbl) { - // this is an exogenous data entry - treat as a table on time and give the table value 1 at all times if we have - // that info - tbl = new ExpressionTable(this->pSymbolNameSpace); - tbl->AddPair(0, 1); - tbl->AddPair(1, 1); - Variable *var = this->FindVariable("TIME"); - if (!var) - var = new Variable(this->pSymbolNameSpace, "TIME"); - ex = new ExpressionVariable(this->pSymbolNameSpace, var, NULL); - } - if (legacy) - tbl->TransformLegacy(); - if (!ex) - return new Equation(pSymbolNameSpace, lhs, tbl, '('); - // with lookup is just a table embedded in a variable - actually the norm for XMILE - // Function* wl = static_cast(pSymbolNameSpace->Find("WITH LOOKUP")); - Expression *rhs = new ExpressionLookup(pSymbolNameSpace, ex, tbl); - return new Equation(pSymbolNameSpace, lhs, rhs, '='); -} - -/* a full eq cleans up the temporary memory space and - also assigns the equation to the Variable - not that inside - of macros there is a separate symbol space this is going - against */ -void DynamoParse::AddFullEq(Equation *eq, int type) { - pSymbolNameSpace->ConfirmAllAllocations(); // now independently allocated - pActiveVar = eq->GetVariable(); - if (!_model->Groups().empty() && pActiveVar->GetAllEquations().empty() && !bInMacro) { - _model->Groups().back()->vVariables.push_back(pActiveVar); - pActiveVar->SetGroup(_model->Groups().back()); - } - if (type == DPTT_level) { - pActiveVar->AddEq(eq, false); - } else if (type == DPTT_init) - pActiveVar->AddEq(eq, true); - else - pActiveVar->AddEq(eq, false); -} - -int DynamoParse::yyerror(const char *str) { - // Set error state instead of throwing exception - has_error_ = true; - last_error_ = str; - // Return 1 to signal error to the parser - return 1; -} - -static std::string compress_whitespace(const std::string &s) { - std::string rval; - const char *tv = s.c_str(); - for (; *tv; tv++) { - if (*tv != ' ' && *tv != '\t' && *tv != '\n' && *tv != '\r' && !isdigit(*tv)) - break; - } - for (; *tv; tv++) { - if (*tv == ' ' || *tv == '\t' || *tv == '\n' || *tv == '\r') { - rval.push_back('_'); - for (; tv[1]; tv++) { - if (tv[1] != ' ' && tv[1] != '\t' && tv[1] != '\n' && tv[1] != '\r') - break; - } - } else if ((*tv >= 'A' && *tv <= 'Z') || (*tv >= 'a' && *tv <= 'z') || isdigit(*tv)) - rval.push_back(*tv); // otherwise ignore - else - break; // start skipping at any special character - } - while (rval.back() == '_') - rval.pop_back(); - return rval; -} - -bool DynamoParse::ProcessFile(const std::string &filename, const char *contents, size_t contentsLen) { - sFilename = filename; - - // Clear any previous error state - ClearError(); - - if (true) { - bool noerr = true; - mDynamoLex.Initialize(contents, contentsLen); - // now we call the bison built parser which will call back to DynamoLex - // for the tokenizing - - int rval; - while (true) { - rval = 0; - try { - mDynamoLex.GetReady(); - rval = dpyyparse(); - - // Check if an error occurred during parsing - if (has_error_) { - log("%s\n", last_error_.c_str()); - log("Error at line %d position %d in file %s\n", mDynamoLex.LineNumber(), mDynamoLex.Position(), - sFilename.c_str()); - log(".... skipping the associated variable and looking for the next usable content.\n"); - pSymbolNameSpace->DeleteAllUnconfirmedAllocations(); - noerr = false; - ClearError(); // Clear for next iteration - if (!FindNextEq(false)) - break; - continue; - } - if (rval == DPTT_eoq) { // some combination of comment and units probably follows - if (!FindNextEq(true)) - break; - } else if (rval == DPTT_groupstar) { - size_t depth = 0; - std::string *cur = mDynamoLex.CurToken(); - while (depth < cur->size() && cur->at(depth) == '*') - depth++; - std::string name = cur->substr(depth); - ModelGroup *group_owner = NULL; - for (std::vector::const_iterator it = _model->Groups().end(); - it-- != _model->Groups().begin();) { - if ((*it)->iDepth < static_cast(depth)) { - group_owner = (*it); - break; - } - } - _model->Groups().push_back(new ModelGroup(name, group_owner, depth)); - } else if (rval == DPTT_specs) { - ParseSpecs(); - } else if (rval == DPTT_save) { - ParseSave(); - } else { - log("Unknown terminal token %d\n", rval); - if (!FindNextEq(false)) - break; - } - - } catch (DynamoParseSyntaxError &e) { - log("%s\n", e.str.c_str()); - log("Error at line %d position %d in file %s\n", mDynamoLex.LineNumber(), mDynamoLex.Position(), - sFilename.c_str()); - log(".... skipping the associated variable and looking for the next usable content.\n"); - pSymbolNameSpace->DeleteAllUnconfirmedAllocations(); - noerr = false; - if (!FindNextEq(false)) - break; - - } catch (...) { - // Log unknown exceptions to aid diagnosis and ensure visibility via C API - log("Unknown exception while parsing (Dynamo) at line %d position %d in file %s\n", mDynamoLex.LineNumber(), - mDynamoLex.Position(), sFilename.c_str()); - log(".... skipping the associated variable and looking for the next usable content.\n"); - pSymbolNameSpace->DeleteAllUnconfirmedAllocations(); - noerr = false; - if (!FindNextEq(false)) - break; - } - } - _model->SetMacroFunctions(mMacroFunctions); - - if (bLongName) { - // try to replace variable names with long names from the documentaion - std::vector vars = _model->GetVariables(NULL); // all symbols that are variables - for (Variable *var : vars) { - std::string alt = compress_whitespace(var->Comment()); - if (!alt.empty() && alt.size() < 80 && pSymbolNameSpace->Rename(var, alt)) { - var->SetAlternateName(alt); - } - } - } - if (!noerr) { - log("warning: writing output file, but we had errors. check the result carefully.\n"); - } - return true; // got something - try to put something out - } else - return false; -} - -char *DynamoParse::GetIntChar(char *s, int &val, char c) { - char *tv; - for (tv = s; *tv; tv++) { - if (*tv == c) { - *tv++ = '\0'; - break; - } - } - val = atoi(s); - return tv; -} -char *DynamoParse::GetInt(char *s, int &val) { - char *tv; - for (tv = s; *tv; tv++) { - if (*tv == ',') { - *tv++ = '\0'; - break; - } - } - val = atoi(s); - return tv; -} -char *DynamoParse::GetString(char *s, std::string &name) { - char *tv = s; - if (*tv == '\"') { - for (tv++; *tv; tv++) { - if (*tv == '\"') { - tv++; - assert(*tv == ','); - *tv++ = '\0'; - break; - } else if (*tv == '\\' && tv[1] == '\"') - tv++; - } - } else { - for (tv = s; *tv; tv++) { - if (*tv == ',') { - *tv++ = '\0'; - break; - } - } - } - name = s; - return tv; -} - -Variable *DynamoParse::FindVariable(const std::string &name) { - Variable *var = static_cast(pSymbolNameSpace->Find(name)); - if (var && var->isType() == Symtype_Variable) - return var; - return NULL; -} - -Variable *DynamoParse::InsertVariable(const std::string &name) { - Variable *var = static_cast(pSymbolNameSpace->Find(name)); - if (var && var->isType() != Symtype_Variable && var->isType() != Symtype_Function) { - has_error_ = true; - last_error_ = "Type meaning mismatch for " + name; - return nullptr; - } - if (!var) { - var = new Variable(pSymbolNameSpace, name); - // this will insert it into the name space for hash lookup as well - } - return var; -} -Units *DynamoParse::InsertUnits(const std::string &name) { - std::string uname = ">" + name; // an illegal variable name since we allow the same names to be used for vars and - // units - could use a separate namespace - Units *u = static_cast(pSymbolNameSpace->Find(uname)); - if (u && u->isType() != Symtype_Units) { - has_error_ = true; - last_error_ = "Type meaning mismatch for " + name; - return nullptr; - } - if (!u) { - u = new Units(pSymbolNameSpace, uname); - } - return u; -} - -UnitExpression *DynamoParse::InsertUnitExpression(Units *u) { - UnitExpression *uni = new UnitExpression(pSymbolNameSpace, u); - return uni; -} - -// find the beginning of the next equation - for error recovery -bool DynamoParse::FindNextEq(bool want_comment) { - if (want_comment && this->pActiveVar) { - std::string units; - std::string comment = mDynamoLex.GetComment(units); - if (!comment.empty()) // multile appearances okay - take last non empty - this->pActiveVar->SetComment(comment); - if (!units.empty()) - this->pActiveVar->SetUnitsString(units); - } else { - // consume the rest of the current line then try again - mDynamoLex.ConsumeCurrentLine(); - } - // just zip through to the first | then whatever follows is it - return mDynamoLex.FindStartToken(); -} - -LeftHandSide *DynamoParse::AddExceptInterp(ExpressionVariable *var, SymbolListList *except, int interpmode) { - return new LeftHandSide(pSymbolNameSpace, var, NULL, except, interpmode); -} -SymbolList *DynamoParse::SymList(SymbolList *in, Variable *add, bool bang, Variable *end) { - SymbolList *sl; - if (in) - sl = in->Append(add, bang); - else - sl = new SymbolList(pSymbolNameSpace, add, bang); - if (end) { /* actually shortcut for (axx-ayy) */ - int i, j; - Variable *v; - std::string start = add->GetName(); - std::string finish = end->GetName(); - for (i = start.length(); --i > 0;) { - if (start[i - 1] < '0' || start[i - 1] > '9') - break; - } - for (j = finish.length(); --j > 0;) { - if (finish[j - 1] < '0' || finish[j - 1] > '9') - break; - } - int low = atoi(start.c_str() + i); - int high = atoi(finish.c_str() + j); - if (i != j || start.compare(0, j, finish, 0, j) || low >= high) { - has_error_ = true; - last_error_ = "Bad subscript range specification"; - delete sl; - return nullptr; - } - start.erase(i, std::string::npos); - for (i = low + 1; i < high; i++) { - finish = start + std::to_string(i); - v = static_cast(pSymbolNameSpace->Find(finish)); - if (!v) - v = new Variable(pSymbolNameSpace, finish); - sl->Append(v, bang); - } - sl->Append(end, bang); - } - return sl; -} - -SymbolList *DynamoParse::MapSymList(SymbolList *in, Variable *range, SymbolList *list) { - list->SetMapRange(range); - if (in) { - in->Append(list); - return in; - } - return list; -} -UnitExpression *DynamoParse::UnitsDiv(UnitExpression *num, UnitExpression *denom) { - return num->Divide(denom); -} -UnitExpression *DynamoParse::UnitsMult(UnitExpression *f, UnitExpression *s) { - return f->Multiply(s); -} -UnitExpression *DynamoParse::UnitsRange(UnitExpression *e, double minval, double maxval, double increment) { - if (e == NULL) { - Units *units = DPObject->InsertUnits("1"); - if (units) { - e = DPObject->InsertUnitExpression(units); - } - } - e->SetRange(minval, maxval, increment); - return e; -} - -SymbolListList *DynamoParse::ChainSublist(SymbolListList *sll, SymbolList *nsl) { - if (!sll) - sll = new SymbolListList(pSymbolNameSpace, nsl); - else - sll->Append(nsl); - return sll; -} -ExpressionList *DynamoParse::ChainExpressionList(ExpressionList *el, Expression *e) { - if (!el) - el = new ExpressionList(pSymbolNameSpace); - return el->Append(e); -} -Expression *DynamoParse::NumExpression(double num) { - return new ExpressionNumber(pSymbolNameSpace, num); -} -Expression *DynamoParse::LiteralExpression(const char *lit) { - return new ExpressionLiteral(pSymbolNameSpace, lit); -} -ExpressionVariable *DynamoParse::VarExpression(Variable *var, SymbolList *subs) { - return new ExpressionVariable(pSymbolNameSpace, var, subs); -} -ExpressionSymbolList *DynamoParse::SymlistExpression(SymbolList *subs, SymbolList *map) { - return new ExpressionSymbolList(pSymbolNameSpace, subs, map); -} -Expression *DynamoParse::OperatorExpression(int oper, Expression *exp1, Expression *exp2) { - switch (oper) { - case '*': - return new ExpressionMultiply(pSymbolNameSpace, exp1, exp2); - case '/': - return new ExpressionDivide(pSymbolNameSpace, exp1, exp2); - case '+': - if (!exp1 && exp2 && exp2->GetType() == EXPTYPE_Number) - return exp2; /* unary plus just ignore */ - return new ExpressionAdd(pSymbolNameSpace, exp1, exp2); - case '-': - if (!exp1) { - if (exp2 && exp2->GetType() == EXPTYPE_Number) { - exp2->FlipSign(); - return exp2; - } - return new ExpressionUnaryMinus(pSymbolNameSpace, exp2, NULL); - } - return new ExpressionSubtract(pSymbolNameSpace, exp1, exp2); - case '^': - return new ExpressionPower(pSymbolNameSpace, exp1, exp2); - case '(': - assert(!exp2); - return new ExpressionParen(pSymbolNameSpace, exp1, NULL); - case '<': - case '>': - case DPTT_le: - case DPTT_ge: - case DPTT_ne: - case DPTT_and: - case DPTT_or: - case '=': - return new ExpressionLogical(pSymbolNameSpace, exp1, exp2, oper); - case DPTT_not: - assert(exp2 == NULL); - return new ExpressionLogical(pSymbolNameSpace, NULL, exp1, oper); - default: - has_error_ = true; - last_error_ = "Unknown operator internal error "; - return nullptr; - } -} -Expression *DynamoParse::FunctionExpression(Function *func, ExpressionList *eargs) { - if (func->IsIntegrator()) { - if (eargs->Length() != 1) { - has_error_ = true; - last_error_ = "Invalid Level Equation internal error"; - return nullptr; - } - } else if (func->NumberArgs() >= 0 && - ((!eargs && func->NumberArgs() > 0) || (eargs && func->NumberArgs() != eargs->Length()))) { - has_error_ = true; - last_error_ = "Argument count mismatch for "; - last_error_.append(func->GetName()); - return nullptr; - } - if (func->IsMemoryless() && !func->IsTimeDependent()) - return new ExpressionFunction(pSymbolNameSpace, func, eargs); - return new ExpressionFunctionMemory(pSymbolNameSpace, func, eargs); -} -Expression *DynamoParse::LookupExpression(ExpressionVariable *var, ExpressionList *args) { - if (args->Length() == 1) - return new ExpressionLookup(pSymbolNameSpace, var, args->GetExp(0)); - // really an error so we use uknown function - const std::string &name = var->GetVariable()->GetName(); - Function *f = new UnknownFunction(new SymbolNameSpace(), name, args->Length()); - return new ExpressionFunction(pSymbolNameSpace, f, args); -} - -ExpressionTable *DynamoParse::TablePairs(ExpressionTable *table, double x, double y) { - if (!table) - table = new ExpressionTable(pSymbolNameSpace); - table->AddPair(x, y); - return table; -} - -ExpressionTable *DynamoParse::XYTableVec(ExpressionTable *table, double val) { - if (!table) - table = new ExpressionTable(pSymbolNameSpace); - table->AddYVal(val); // fix these after reducing - return table; -} - -ExpressionTable *DynamoParse::TableRange(ExpressionTable *table, double x1, double y1, double x2, double y2) { - table->AddRange(x1, y1, x2, y2); - return table; -} - -void DynamoParse::MacroStart() { - bInMacro = true; - pMainSymbolNameSpace = pSymbolNameSpace; - pSymbolNameSpace = new SymbolNameSpace(); // local name space for macro variables and macro name - macro name will go - // into the main name space on close - ReadyFunctions(); // against this new name space - somewhat duplicative -} - -void DynamoParse::MacroExpression(Variable *name, ExpressionList *margs) { - // the macro functiongoes against the main name space - everything else is local - mMacroFunctions.push_back(new MacroFunction(pMainSymbolNameSpace, pSymbolNameSpace, name->GetName(), margs)); -} -void DynamoParse::MacroEnd() { - pSymbolNameSpace = pMainSymbolNameSpace; - bInMacro = false; -} - -bool DynamoParse::LetterPolarity() const { - return _model->LetterPolarity(); -} -void DynamoParse::SetLetterPolarity(bool set) { - _model->SetLetterPolarity(set); -} - -void DynamoParse::ParseSpecs() { - mDynamoLex.ParseSpecs(_model); -} - -void DynamoParse::ParseSave() { - mDynamoLex.ParseSpecs(NULL); -} diff --git a/src/xmutil/third_party/xmutil/Dynamo/DynamoParse.h b/src/xmutil/third_party/xmutil/Dynamo/DynamoParse.h deleted file mode 100644 index a98f48ccd..000000000 --- a/src/xmutil/third_party/xmutil/Dynamo/DynamoParse.h +++ /dev/null @@ -1,124 +0,0 @@ -#ifndef _XMUTIL_DYNAMO_DYNAMOPARSE_H -#define _XMUTIL_DYNAMO_DYNAMOPARSE_H -#include - -#include "../Function/Function.h" -#include "../Symbol/Equation.h" -#include "../Symbol/Parse.h" -#include "../Symbol/Symbol.h" -#include "../Symbol/Units.h" -#include "DynamoLex.h" - -#define BUFLEN 4096 // for reading sketch info - -class DynamoView; - -class DynamoParseSyntaxError { -public: - std::string str; -}; - -class DynamoParse { -public: - DynamoParse(Model *model); - ~DynamoParse(void); - void ReadyFunctions(); - bool ProcessFile(const std::string &filename, const char *contents, size_t contentsLen); - inline int yylex(void) { - return mDynamoLex.yylex(); - } - int yyerror(const char *str); - Equation *AddEq(LeftHandSide *lhs, Expression *ex, ExpressionList *exl, int tok); - Equation *AddStockEq(LeftHandSide *lhs, ExpressionVariable *var, ExpressionList *exl, int tok); - Equation *AddTable(LeftHandSide *lhs, Expression *ex, ExpressionTable *table, bool legacy); - inline SymbolNameSpace *GetSymbolNameSpace(void) { - return pSymbolNameSpace; - } - Variable *InsertVariable(const std::string &name); - Variable *FindVariable(const std::string &name); - Units *InsertUnits(const std::string &name); - UnitExpression *InsertUnitExpression(Units *u); - void AddFullEq(Equation *eq, int type); - LeftHandSide *AddExceptInterp(ExpressionVariable *var, SymbolListList *except, int interpmode); - SymbolList *SymList(SymbolList *in, Variable *add, bool bang, Variable *end); - SymbolList *MapSymList(SymbolList *in, Variable *range, SymbolList *list); - UnitExpression *UnitsDiv(UnitExpression *num, UnitExpression *denom); - UnitExpression *UnitsMult(UnitExpression *f, UnitExpression *s); - UnitExpression *UnitsRange(UnitExpression *e, double minval, double maxval, double increment); - SymbolListList *ChainSublist(SymbolListList *sll, SymbolList *nsl); - ExpressionList *ChainExpressionList(ExpressionList *el, Expression *e); - Expression *NumExpression(double num); - Expression *LiteralExpression(const char *lit); - ExpressionVariable *VarExpression(Variable *var, SymbolList *subs); - ExpressionSymbolList *SymlistExpression(SymbolList *sym, SymbolList *map); - Expression *OperatorExpression(int oper, Expression *exp1, Expression *exp2); - Expression *FunctionExpression(Function *func, ExpressionList *eargs); - Expression *LookupExpression(ExpressionVariable *var, ExpressionList *args); - ExpressionTable *TablePairs(ExpressionTable *table, double x, double y); - ExpressionTable *XYTableVec(ExpressionTable *table, double val); - ExpressionTable *TableRange(ExpressionTable *table, double x1, double y1, double x2, double y2); - void MacroStart(); - void MacroExpression(Variable *macro, ExpressionList *margs); - void MacroEnd(); - - double Xratio() const { - return _xratio; - } - double Yratio() const { - return _yratio; - } - DynamoLex &Lexer() { - return mDynamoLex; - } - char *GetInt(char *buf, int &val); - char *GetIntChar(char *buf, int &val, char c); - char *GetString(char *buf, std::string &s); - - void SetLongName(bool set) { - bLongName = set; - } - bool LongName() const { - return bLongName; - } - - bool LetterPolarity() const; - void SetLetterPolarity(bool set); - - void ParseSpecs(); - void ParseSave(); - - // Error state management - for C/Rust compatibility - bool HasError() const { - return has_error_; - } - const std::string &GetLastError() const { - return last_error_; - } - void ClearError() { - has_error_ = false; - last_error_.clear(); - } - -private: - // Error state management members - bool has_error_ = false; - std::string last_error_; - - bool FindNextEq(bool want_comment); - Model *_model; - std::string sFilename; - DynamoLex mDynamoLex; - DynamoParseSyntaxError mSyntaxError; - SymbolNameSpace *pSymbolNameSpace; - SymbolNameSpace *pMainSymbolNameSpace; - Variable *pActiveVar; - double _xratio; - double _yratio; - bool bInMacro; - bool bLongName; - std::vector mMacroFunctions; -}; - -extern DynamoParse *DPObject; - -#endif \ No newline at end of file diff --git a/src/xmutil/third_party/xmutil/Dynamo/DynamoParseFunctions.cpp b/src/xmutil/third_party/xmutil/Dynamo/DynamoParseFunctions.cpp deleted file mode 100644 index 9e62f43b4..000000000 --- a/src/xmutil/third_party/xmutil/Dynamo/DynamoParseFunctions.cpp +++ /dev/null @@ -1,89 +0,0 @@ -#include "DynamoParseFunctions.h" - -#include "../Symbol/Parse.h" -#include "../XMUtil.h" -#include "DynamoParse.h" - -void dpyy_addfulleq(Equation *eq, int type) { - return DPObject->AddFullEq(eq, type); -} -Equation *dpyy_addstockeq(LeftHandSide *lhs, ExpressionVariable *v, ExpressionList *exl, int token) { - return DPObject->AddStockEq(lhs, v, exl, token); -} -Equation *dpyy_addeq(LeftHandSide *lhs, Expression *ex, ExpressionList *exl, int token) { - return DPObject->AddEq(lhs, ex, exl, token); -} -Equation *dpyy_add_lookup(LeftHandSide *lhs, Expression *ex, ExpressionTable *tvl, int legacy) { - return DPObject->AddTable(lhs, ex, tvl, legacy != 0); -} -LeftHandSide *dpyy_addexceptinterp(ExpressionVariable *var, SymbolListList *except, int interpmode) { - return DPObject->AddExceptInterp(var, except, interpmode); -} -SymbolList *dpyy_symlist(SymbolList *in, Variable *add, int bang, Variable *end) { - return DPObject->SymList(in, add, !!bang, end); -} -SymbolList *dpyy_mapsymlist(SymbolList *in, Variable *maprange, SymbolList *list) { - return DPObject->MapSymList(in, maprange, list); -} -UnitExpression *dpyy_unitsdiv(UnitExpression *num, UnitExpression *denom) { - return DPObject->UnitsDiv(num, denom); -} -UnitExpression *dpyy_unitsmult(UnitExpression *f, UnitExpression *s) { - return DPObject->UnitsMult(f, s); -} -UnitExpression *dpyy_unitsrange(UnitExpression *f, double minval, double maxval, double increment) { - return DPObject->UnitsRange(f, minval, maxval, increment); -} -SymbolListList *dpyy_chain_sublist(SymbolListList *sll, SymbolList *nsl) { - return DPObject->ChainSublist(sll, nsl); -} -ExpressionList *dpyy_chain_exprlist(ExpressionList *el, Expression *e) { - return DPObject->ChainExpressionList(el, e); -} -Expression *dpyy_num_expression(double num) { - return DPObject->NumExpression(num); -} -Expression *dpyy_literal_expression(const char *lit) { - return DPObject->LiteralExpression(lit); -} -ExpressionVariable *dpyy_var_expression(Variable *var, SymbolList *subs) { - return DPObject->VarExpression(var, subs); -} -ExpressionSymbolList *dpyy_symlist_expression(SymbolList *sym, SymbolList *map) { - return DPObject->SymlistExpression(sym, map); -} -Expression *dpyy_operator_expression(int oper, Expression *exp1, Expression *exp2) { - return DPObject->OperatorExpression(oper, exp1, exp2); -} -Expression *dpyy_function_expression(Function *func, ExpressionList *eargs) { - return DPObject->FunctionExpression(func, eargs); -} -Expression *dpyy_lookup_expression(ExpressionVariable *var, ExpressionList *args) { - return DPObject->LookupExpression(var, args); -} -ExpressionTable *dpyy_tablepair(ExpressionTable *table, double x, double y) { - return DPObject->TablePairs(table, x, y); -} -ExpressionTable *dpyy_tablevec(ExpressionTable *table, double val) { - return DPObject->XYTableVec(table, val); -} -ExpressionTable *dpyy_tablerange(ExpressionTable *table, double x1, double y1, double x2, double y2) { - return DPObject->TableRange(table, x1, y1, x2, y2); -} -void dpyy_macro_start() { - DPObject->MacroStart(); -} -void dpyy_macro_expression(Variable *name, ExpressionList *margs) { - DPObject->MacroExpression(name, margs); -} -void dpyy_macro_end() { - DPObject->MacroEnd(); -} - -/* the default functions called by parser */ -int dpyylex(void) { - return DPObject->yylex(); -} -void dpyyerror(const char *str) { - DPObject->yyerror(str); -} diff --git a/src/xmutil/third_party/xmutil/Dynamo/DynamoParseFunctions.h b/src/xmutil/third_party/xmutil/Dynamo/DynamoParseFunctions.h deleted file mode 100644 index 1254bdd59..000000000 --- a/src/xmutil/third_party/xmutil/Dynamo/DynamoParseFunctions.h +++ /dev/null @@ -1,31 +0,0 @@ -#ifndef _XMUTIL_DYNAMO_DYNAMOPARSEFUNCTIONS_H -#define _XMUTIL_DYNAMO_DYNAMOPARSEFUNCTIONS_H -#include "../Symbol/Parse.h" - -void dpyy_addfulleq(Equation *eq, int type); -Equation *dpyy_addeq(LeftHandSide *lhs, Expression *ex, ExpressionList *exl, int token); -Equation *dpyy_addstockeq(LeftHandSide *lhs, ExpressionVariable *stock, ExpressionList *exl, int token); -Equation *dpyy_add_lookup(LeftHandSide *lhs, Expression *ex, ExpressionTable *tvl, int legacy); -LeftHandSide *dpyy_addexceptinterp(ExpressionVariable *var, SymbolListList *except, int interpmode); -SymbolList *dpyy_symlist(SymbolList *in, Variable *add, int bang, Variable *end); -SymbolList *dpyy_mapsymlist(SymbolList *in, Variable *maprange, SymbolList *list); -UnitExpression *dpyy_unitsdiv(UnitExpression *num, UnitExpression *denom); -UnitExpression *dpyy_unitsmult(UnitExpression *f, UnitExpression *s); -UnitExpression *dpyy_unitsrange(UnitExpression *f, double minval, double maxval, double increment); -SymbolListList *dpyy_chain_sublist(SymbolListList *sll, SymbolList *nsl); -ExpressionList *dpyy_chain_exprlist(ExpressionList *el, Expression *e); -Expression *dpyy_num_expression(double num); -Expression *dpyy_literal_expression(const char *tok); -ExpressionVariable *dpyy_var_expression(Variable *var, SymbolList *subs); -ExpressionSymbolList *dpyy_symlist_expression(SymbolList *subs, SymbolList *map); -Expression *dpyy_operator_expression(int oper, Expression *exp1, Expression *exp2); -Expression *dpyy_function_expression(Function *func, ExpressionList *args); -Expression *dpyy_lookup_expression(ExpressionVariable *var, ExpressionList *args); -ExpressionTable *dpyy_tablepair(ExpressionTable *table, double x, double y); -ExpressionTable *dpyy_tablevec(ExpressionTable *table, double val); -ExpressionTable *dpyy_tablerange(ExpressionTable *table, double x1, double y1, double x2, double y2); -void dpyy_macro_start(); -void dpyy_macro_expression(Variable *name, ExpressionList *margs); -void dpyy_macro_end(); - -#endif diff --git a/src/xmutil/third_party/xmutil/Function/Function.cpp b/src/xmutil/third_party/xmutil/Function/Function.cpp deleted file mode 100644 index 0e8afda2e..000000000 --- a/src/xmutil/third_party/xmutil/Function/Function.cpp +++ /dev/null @@ -1,328 +0,0 @@ -#include "Function.h" - -#include "../Symbol/ExpressionList.h" -#include "../XMUtil.h" - -// model symbol - most variables including subscript ranges but not -// subscript elements which are just -Function::Function(SymbolNameSpace *sns, const std::string &name, int nargs) : Symbol(sns, name) { - assert(nargs < 32); // the way initial argument identification is implemented requires this - iNumberArgs = nargs; -} - -Function::~Function(void) { -} - -MacroFunction::MacroFunction(SymbolNameSpace *sns, SymbolNameSpace *local, const std::string &name, - ExpressionList *margs) - : Function(sns, name, margs->Length()), pSymbolNameSpace(local), mArgs(margs) { -} - -std::string MacroFunction::ComputableName(void) { - return SpaceToUnderBar(this->GetName()); -} - -bool Function::CheckComputedList(ContextInfo *info, ExpressionList *arg) { - return arg->CheckComputed(info, 0xffffffff); -} -void Function::OutputComputable(ContextInfo *info, ExpressionList *arg) { - *info << ComputableName(); - if (arg) { - *info << "("; - arg->OutputComputable(info, 0xffffffff); - *info << ")"; - } -} - -void UnknownFunction::OutputComputable(ContextInfo *info, ExpressionList *arg) { - *info << "{Untranslated function used}"; - *info << sName; - Function::OutputComputable(info, arg); -} - -void FunctionVectorLookup::OutputComputable(ContextInfo *info, ExpressionList *arg) { - // could try to figure this one out - - *info << "{try INTERPORATE or just used arrays with variable arguments for indices}"; - Function::OutputComputable(info, arg); -} - -void FunctionElmCount::OutputComputable(ContextInfo *info, ExpressionList *arg) { - if (arg && arg->Length() == 1 && arg->GetExp(0)->GetType() == EXPTYPE_Variable) { - *info << "SIZE("; - Variable *var = static_cast(arg->GetExp(0))->GetVariable(); - *info << var->GetName(); - *info << ")"; - return; - } - Function::OutputComputable(info, arg); -} - -void FunctionTimeBase::OutputComputable(ContextInfo *info, ExpressionList *arg) { - if (arg->Length() == 2) { - Expression *exp1 = arg->GetExp(0); - exp1->OutputComputable(info); - *info << " + ("; - Expression *exp2 = arg->GetExp(1); - exp2->OutputComputable(info); - *info << ") * Time"; - } else { - Function::OutputComputable(info, arg); - } -} - -bool FunctionMemoryBase::CheckComputedList(ContextInfo *info, ExpressionList *arg) { - if (info->GetComputeType() == CF_initial) - return arg->CheckComputed(info, iInitArgMark); - else - return arg->CheckComputed(info, iActiveArgMark); -} -void FunctionMemoryBase::OutputComputable(ContextInfo *info, ExpressionList *arg) { - if (info->GetComputeType() == CF_xmile_output) { - const std::string &fname = this->GetName(); - if (fname == "INITIAL") { - *info << "INIT("; - arg->OutputComputable(info, iInitArgMark); - *info << ")"; - } else if (this->IsIntegrator() || info->InitEqn()) - arg->OutputComputable(info, iInitArgMark); - else - arg->OutputComputable(info, iActiveArgMark); - return; - } - - if (info->GetComputeType() == CF_initial) { - *info << ComputableNameInit() << "("; - arg->OutputComputable(info, iInitArgMark); - } else { - *info << ComputableName() << "("; - arg->OutputComputable(info, iActiveArgMark); - } - *info << ")"; -} -/* a simple utility function to flip the bits so we can use BOOST_BINARY - in the intuitive right to left manner for initial value arguments */ -unsigned FunctionMemoryBase::BitFlip(unsigned bits) { - unsigned newbits = 0; - int i; - for (i = 0; i < iNumberArgs; i++) { - if (bits & (1 << i)) - newbits |= (1 << (iNumberArgs - i - 1)); - } - return newbits; -} - -void FunctionSampleIfTrue::OutputComputable(ContextInfo *info, ExpressionList *arg) { - if (arg->Length() == 3) { - *info << "( IF "; - info->SetSelfIsPrevious(true); - const_cast((*arg)[0])->OutputComputable(info); // OutputComputable should really be const - info->SetSelfIsPrevious(false); - *info << " THEN "; - const_cast((*arg)[1])->OutputComputable(info); // OutputComputable should really be const - *info << " ELSE PREVIOUS(SELF, "; - const_cast((*arg)[2])->OutputComputable(info); // OutputComputable should really be const - *info << ") )"; - return; - } - Function::OutputComputable(info, arg); -} - -void FunctionPulse::OutputComputable(ContextInfo *info, ExpressionList *arg) { - if (arg->Length() == 2) { - *info << "( IF TIME >= ("; - const_cast((*arg)[0])->OutputComputable(info); // OutputComputable should really be const - *info << ") AND TIME < (("; - const_cast((*arg)[0])->OutputComputable(info); // OutputComputable should really be const - *info << ") + MAX(DT,"; - const_cast((*arg)[1])->OutputComputable(info); // OutputComputable should really be const - *info << ")) THEN 1 ELSE 0 )"; - return; - } - Function::OutputComputable(info, arg); -} - -void FunctionPulseTrain::OutputComputable(ContextInfo *info, ExpressionList *arg) { - if (arg->Length() == 4) { - *info << "( IF TIME >= ("; - const_cast((*arg)[0])->OutputComputable(info); // OutputComputable should really be const - *info << ") AND TIME <= ("; - const_cast((*arg)[3])->OutputComputable(info); // OutputComputable should really be const - *info << ") AND (TIME - ("; - const_cast((*arg)[0])->OutputComputable(info); // OutputComputable should really be const - *info << ")) MOD ("; - const_cast((*arg)[2])->OutputComputable(info); // OutputComputable should really be const - *info << ") < ("; - const_cast((*arg)[1])->OutputComputable(info); // OutputComputable should really be const - *info << ") THEN 1 ELSE 0 )"; - return; - } - Function::OutputComputable(info, arg); -} - -void FunctionQuantum::OutputComputable(ContextInfo *info, ExpressionList *arg) { - if (arg->Length() == 2) { - *info << "("; - const_cast((*arg)[1])->OutputComputable(info); // OutputComputable should really be const - *info << ")*INT(("; - const_cast((*arg)[0])->OutputComputable(info); // OutputComputable should really be const - *info << ")/("; - const_cast((*arg)[1])->OutputComputable(info); // OutputComputable should really be const - *info << "))"; - return; - } - Function::OutputComputable(info, arg); -} - -void FunctionRandomNormal::OutputComputable(ContextInfo *info, ExpressionList *arg) { - if (arg->Length() == 5) { - *info << "NORMAL("; - const_cast((*arg)[2])->OutputComputable(info); // OutputComputable should really be const - *info << ", "; - const_cast((*arg)[3])->OutputComputable(info); // OutputComputable should really be const - *info << ", "; - const_cast((*arg)[4])->OutputComputable(info); // OutputComputable should really be const - *info << ", "; - const_cast((*arg)[0])->OutputComputable(info); // OutputComputable should really be const - *info << ", "; - const_cast((*arg)[1])->OutputComputable(info); // OutputComputable should really be const - *info << ")"; - return; - } - Function::OutputComputable(info, arg); -} - -void FunctionRandomPoisson::OutputComputable(ContextInfo *info, ExpressionList *arg) { - if (arg->Length() == 6) { - *info << "POISSON(("; - const_cast((*arg)[2])->OutputComputable(info); // OutputComputable should really be const - *info << ")/DT, "; - const_cast((*arg)[5])->OutputComputable(info); // OutputComputable should really be const - *info << ", "; - const_cast((*arg)[0])->OutputComputable(info); // OutputComputable should really be const - *info << ", "; - const_cast((*arg)[1])->OutputComputable(info); // OutputComputable should really be const - *info << ") * "; - const_cast((*arg)[4])->OutputComputable(info); // OutputComputable should really be const - *info << " + "; - const_cast((*arg)[3])->OutputComputable(info); // OutputComputable should really be const - return; - } - Function::OutputComputable(info, arg); -} - -void FunctionIfThenElse::OutputComputable(ContextInfo *info, ExpressionList *arg) { - if (arg->Length() == 3) { - *info << "( IF "; - const_cast((*arg)[0])->OutputComputable(info); // OutputComputable should really be const - *info << " THEN "; - const_cast((*arg)[1])->OutputComputable(info); // OutputComputable should really be const - *info << " ELSE "; - const_cast((*arg)[2])->OutputComputable(info); // OutputComputable should really be const - *info << " )"; - return; - } - Function::OutputComputable(info, arg); -} - -void FunctionDelayN::OutputComputable(ContextInfo *info, ExpressionList *arg) { - if (arg->Length() == 4) { - *info << "DELAYN("; - const_cast((*arg)[0])->OutputComputable(info); - *info << ","; - const_cast((*arg)[1])->OutputComputable(info); - *info << ","; - const_cast((*arg)[3])->OutputComputable(info); - *info << ","; - const_cast((*arg)[2])->OutputComputable(info); - *info << ")"; - } -} -void FunctionSmoothN::OutputComputable(ContextInfo *info, ExpressionList *arg) { - if (arg->Length() == 4) { - *info << "SMTHN("; - const_cast((*arg)[0])->OutputComputable(info); - *info << ","; - const_cast((*arg)[1])->OutputComputable(info); - *info << ","; - const_cast((*arg)[3])->OutputComputable(info); - *info << ","; - const_cast((*arg)[2])->OutputComputable(info); - *info << ")"; - } -} - -void FunctionLog::OutputComputable(ContextInfo *info, ExpressionList *arg) { - if (arg->Length() == 2) { - *info << "(LN("; - const_cast((*arg)[0])->OutputComputable(info); // OutputComputable should really be const - *info << ") / LN("; - const_cast((*arg)[1])->OutputComputable(info); // OutputComputable should really be const - *info << "))"; - return; - } - Function::OutputComputable(info, arg); -} - -void FunctionAllocateByPriority::OutputComputable(ContextInfo *info, ExpressionList *arg) { - if (arg->Length() == 5) { - *info << "ALLOCATE("; - Expression *supply = arg->GetExp(4); - supply->OutputComputable(info); - *info << ", "; - Expression *demand = arg->GetExp(0); - if (demand->GetType() == EXPTYPE_Variable) { - ExpressionVariable *expvar = static_cast(demand); - SymbolList *subs = expvar->GetSubs(); - if (subs) { - int len = subs->Length(); - if (len > 0) { - const SymbolList::SymbolListEntry &blah = (*subs)[len - 1]; - *info << blah.u.pSymbol->GetName(); - } - } - } - *info << ", "; - info->SetWantFinalStar(true); - demand->OutputComputable(info); - *info << ", "; - Expression *priority = arg->GetExp(1); - priority->OutputComputable(info); - *info << ", "; - info->SetWantFinalStar(false); - Expression *width = arg->GetExp(3); - width->OutputComputable(info); - *info << ")"; - } else { - Function::OutputComputable(info, arg); - } -} - -#ifdef WANT_EVAL_STUFF -double FunctionMax::Eval(Expression *from, ExpressionList *arg, ContextInfo *info) { - double a1 = arg->GetExp(0)->Eval(info); - double a2 = arg->GetExp(1)->Eval(info); - return a1 > a2 ? a1 : a2; -} -double FunctionMin::Eval(Expression *from, ExpressionList *arg, ContextInfo *info) { - double a1 = arg->GetExp(0)->Eval(info); - double a2 = arg->GetExp(1)->Eval(info); - return a1 < a2 ? a1 : a2; -} -double FunctionInteg::Eval(Expression *from, ExpressionList *arg, ContextInfo *info) { - if (info->GetComputeType() == CF_initial) - return arg->GetExp(1)->Eval(info); // initialization - return arg->GetExp(0)->Eval(info); - return 0; -} -double FunctionPulse::Eval(Expression *from, ExpressionList *arg, ContextInfo *info) { - double s = arg->GetExp(0)->Eval(info); - double dt = info->GetDT(); - double w = arg->GetExp(1)->Eval(info); - if (w < dt) - w = dt; - double t = info->GetTime(); - if (t > s - dt / 4.0 && t < s + w - dt / 4.0) - return 1.0; - return 0; -} -#endif diff --git a/src/xmutil/third_party/xmutil/Function/Function.h b/src/xmutil/third_party/xmutil/Function/Function.h deleted file mode 100644 index 9c16f6c91..000000000 --- a/src/xmutil/third_party/xmutil/Function/Function.h +++ /dev/null @@ -1,477 +0,0 @@ -#ifndef _XMUTIL_SYMBOL_FUNCTION_H -#define _XMUTIL_SYMBOL_FUNCTION_H -#include "../Symbol/Symbol.h" -#include "State.h" - -class Expression; /* forward declaration */ -class ExpressionList; // forward -class UnitExpression; - -/* abstract class - every function has its own subclass - defined here with bodies in Function.cpp or in their own - files depending on complexity */ - -class Function : public Symbol { -public: - Function(SymbolNameSpace *sns, const std::string &name, int narg); - virtual ~Function(void) = 0; - SYMTYPE isType(void) { - return Symtype_Function; - } - virtual bool AsKeyword(void) { - return false; - } // for the parser - treats name as keyword not function - virtual bool IsMemoryless(void) { - return true; - } - virtual bool IsDelay(void) { - return false; - } - virtual bool IsTimeDependent(void) { - return false; - } - virtual double Eval(Expression *ex, ExpressionList *arg, ContextInfo *info) { - return 0; - } // make this pure virtual if finishing engine is required - virtual bool CheckComputedList(ContextInfo *info, ExpressionList *arg); - virtual void OutputComputable(ContextInfo *info, ExpressionList *arg); - virtual std::string ComputableName(void) { - return ""; - } - virtual std::string ComputableNameInit(void) { - return ""; - } - virtual bool IsActiveInit() { - return false; - } - virtual bool IsIntegrator() const { - return false; - } - virtual bool IsTableCall() const { - return false; - } - int NumberArgs(void) { - return iNumberArgs; - } - -protected: - int iNumberArgs; -}; - -class DFunction : public Function { -public: - DFunction(SymbolNameSpace *sns, const std::string &name, int narg) : Function(sns, name, narg) { - } - virtual bool IsDelay(void) override { - return true; - } -}; - -class UnknownFunction : public Function { -public: - UnknownFunction(SymbolNameSpace *sns, const std::string &name, int narg) : Function(sns, name, narg), sName(name) { - } - ~UnknownFunction() { - } - virtual void OutputComputable(ContextInfo *info, ExpressionList *arg) override; - -private: - std::string sName; -}; - -class FunctionMemoryBase : public Function { -public: - FunctionMemoryBase(SymbolNameSpace *sns, const std::string &name, int narg, unsigned actarg, unsigned iniarg) - : Function(sns, name, narg) { - iActiveArgMark = BitFlip(actarg); - iInitArgMark = BitFlip(iniarg); - } - ~FunctionMemoryBase(void) { - } - unsigned BitFlip(unsigned bits); - virtual bool IsMemoryless(void) override { - return false; - } - virtual bool IsDelay(void) override { - return true; - } - virtual bool CheckComputedList(ContextInfo *info, ExpressionList *arg) override; - virtual void OutputComputable(ContextInfo *info, ExpressionList *arg) override; - -private: - unsigned iInitArgMark; - unsigned iActiveArgMark; -}; - -class MacroFunction : public Function { -public: - class EqUnitPair { - public: - EqUnitPair(Equation *eq, UnitExpression *un) : equation(eq), units(un) { - } - Equation *equation; - UnitExpression *units; - }; - SymbolNameSpace *NameSpace() { - return pSymbolNameSpace; - } - MacroFunction(SymbolNameSpace *sns, SymbolNameSpace *local, const std::string &name, ExpressionList *margs); - ~MacroFunction() { - delete pSymbolNameSpace; - } - void AddEq(Equation *equation, UnitExpression *units) { - mEquations.push_back(EqUnitPair(equation, units)); - } - ExpressionList *Args() { - return mArgs; - } - virtual std::string ComputableName(void); - -private: - SymbolNameSpace *pSymbolNameSpace; // local - MacroFunction(const MacroFunction &other); - ExpressionList *mArgs; - std::vector mEquations; -}; - -#define FSubclassKeyword(name, xname, narg) \ - class name : public Function { \ - public: \ - name(SymbolNameSpace *sns) : Function(sns, xname, narg) { \ - ; \ - } \ - ~name(void) { \ - } \ - std::string ComputableName(void) override { \ - return " ?? "; \ - } \ - bool AsKeyword(void) override { \ - return true; \ - } \ - } - -#define FSubclassStart(name, xname, narg, cname) \ - class name : public Function { \ - public: \ - name(SymbolNameSpace *sns) : Function(sns, xname, narg) { \ - ; \ - } \ - ~name(void) { \ - } \ - std::string ComputableName(void) override { \ - return cname; \ - } \ - \ - private: - -#define FSubclass(name, xname, narg, cname) \ - FSubclassStart(name, xname, narg, cname) \ - } - -#define DFSubclassStart(name, xname, narg, cname) \ - class name : public DFunction { \ - public: \ - name(SymbolNameSpace *sns) : DFunction(sns, xname, narg) { \ - ; \ - } \ - ~name(void) { \ - } \ - std::string ComputableName(void) { \ - return cname; \ - } \ - \ - private: - -#define DFSubclass(name, xname, narg, cname) \ - DFSubclassStart(name, xname, narg, cname) \ - } - -#define FSubclassMemoryStart(name, xname, narg, actarg, iniarg, cnamea, cnamei) \ - class name : public FunctionMemoryBase { \ - public: \ - name(SymbolNameSpace *sns) : FunctionMemoryBase(sns, xname, narg, actarg, iniarg) { \ - } \ - ~name(void) { \ - } \ - virtual std::string ComputableName(void) override { \ - return cnamea; \ - } \ - virtual std::string ComputableNameInit(void) override { \ - return cnamei; \ - } \ - \ - private: - -#define FSubclassMemory(name, xname, narg, actarg, iniarg, cnamea, cnamei) \ - FSubclassMemoryStart(name, xname, narg, actarg, iniarg, cnamea, cnamei) \ - } \ - ; - -#define FSubclassTimeStart(name, xname, narg, cname) \ - class name : public Function { \ - public: \ - name(SymbolNameSpace *sns) : Function(sns, xname, narg) { \ - ; \ - } \ - ~name(void) { \ - } \ - bool IsTimeDependent(void) override { \ - return true; \ - } \ - std::string ComputableName(void) override { \ - return cname; \ - } \ - \ - private: - -#define FSubclassTime(name, xname, narg, cname) \ - FSubclassTimeStart(name, xname, narg, cname) \ - } \ - ; - -FSubclass(FunctionAbs, "ABS", 1, "ABS"); -FSubclass(FunctionExp, "EXP", 1, "EXP"); -FSubclass(FunctionSqrt, "SQRT", 1, "SQRT"); - -FSubclass(FunctionCosine, "COS", 1, "COS"); -FSubclass(FunctionTangent, "TAN", 1, "TAN"); -FSubclass(FunctionSine, "SIN", 1, "SIN"); -FSubclass(FunctionArcCosine, "ARCCOS", 1, "ARCCOS"); -FSubclass(FunctionArcSine, "ARCSIN", 1, "ARCSIN"); -FSubclass(FunctionArcTangent, "ARCTAN", 1, "ARCTAN"); -FSubclass(FunctionInterger, "INTEGER", 1, "INT"); - -FSubclass(FunctionMax, "MAX", 2, "MAX"); -FSubclass(FunctionMin, "MIN", 2, "MIN"); -FSubclass(FunctionZidz, "ZIDZ", 2, "SAFEDIV"); -FSubclass(FunctionXidz, "XIDZ", 3, "SAFEDIV"); -FSubclass(FunctionLookupInv, "LOOKUP INVERT", 2, "LOOKUPINV"); -FSubclass(FunctionWithLookup, "WITH LOOKUP", 3, "WITH_LOOKUP"); -FSubclass(FunctionSum, "SUM", 1, "SUM"); -FSubclass(FunctionProd, "PROD", 1, "PROD"); -FSubclass(FunctionVMax, "VMAX", 1, "MAX"); -FSubclass(FunctionVMin, "VMIN", 1, "MIN"); -FSubclass(FunctionVectorSelect, "VECTOR SELECT", 5, "VECTOR SELECT"); -FSubclass(FunctionVectorElmMap, "VECTOR ELM MAP", 2, "VECTOR ELM MAP"); -FSubclass(FunctionVectorSortOrder, "VECTOR SORT ORDER", 2, "VECTOR SORT ORDER"); -FSubclass(FunctionGame, "GAME", 1, ""); // don't need this -FSubclass(FunctionRandom01, "RANDOM 0 1", 0, "UNIFORM(0,1)"); -FSubclass(FunctionRandomUniform, "RANDOM UNIFORM", 3, "UNIFORM"); -FSubclass(FunctionRandomPink, "RANDOM PINK NOISE", 4, "NORMALPINK"); - -FSubclass(FunctionNAN, "A FUNCTION OF", -1, "NAN"); - -// actually memory but no init - or init - does not matter for translation -DFSubclass(FunctionSmooth, "SMOOTH", 2, "SMTH1"); -DFSubclass(FunctionSmoothI, "SMOOTHI", 3, "SMTH1"); -DFSubclass(FunctionSmooth3, "SMOOTH3", 2, "SMTH3"); -DFSubclass(FunctionSmooth3I, "SMOOTH3I", 3, "SMTH3"); -DFSubclass(FunctionTrend, "TREND", 3, "TREND"); -DFSubclass(FunctionFrcst, "FORECAST", 3, "FORCST"); -DFSubclass(FunctionDelay1, "DELAY1", 2, "DELAY1"); -DFSubclass(FunctionDelay1I, "DELAY1I", 3, "DELAY1"); -DFSubclass(FunctionDelay3, "DELAY3", 2, "DELAY3"); -DFSubclass(FunctionDelay3I, "DELAY3I", 3, "DELAY3"); -DFSubclass(FunctionDelay, "DELAY FIXED", 3, "DELAY"); -DFSubclass(FunctionNPV, "NPV", 4, "NPV"); - -// done as macros -FSubclass(FunctionDelayConveyor, "DELAY CONVEYOR", 6, "DELAY_CONVEYOR"); -// - this one is fake - return NaN -FSubclass(FunctionVectorReorder, "VECTOR REORDER", 2, "VECTOR_REORDER"); -class FunctionVectorLookup : public Function { -public: - FunctionVectorLookup(SymbolNameSpace *sns) : Function(sns, "VECTOR LOOKUP", 5) { - } - ~FunctionVectorLookup(void) { - } - std::string ComputableName(void) override { - return "VECTOR LOOKUP"; - } - virtual void OutputComputable(ContextInfo *info, ExpressionList *arg) override; - -private: -}; -FSubclassStart(FunctionElmCount, "ELMCOUNT", 1, "SIZE"); -virtual void OutputComputable(ContextInfo *info, ExpressionList *arg) override; -} -; -FSubclass(FunctionModulo, "MODULO", 2, "MODULO"); -FSubclass(FunctionGetDataAtTime, "GET DATA AT TIME", 2, "GET_DATA_AT_TIME"); -FSubclass(FunctionGetDataLastTime, "GET DATA LAST TIME", 1, "GET_DATA_LAST_TIME"); -FSubclass(FunctionLookupArea, "LOOKUP AREA", 3, "LOOKUP_AREA"); -FSubclass(FunctionLookupExtrapolate, "LOOKUP EXTRAPOLATE", 2, "LOOKUP"); // changes the graphical -FSubclass(FunctionLookupForward, "LOOKUP FORWARD", 2, "LOOKUP_FORWARD"); -FSubclass(FunctionLookupBackward, "LOOKUP BACKWARD", 2, "LOOKUP_BACKWARD"); -FSubclassStart(FunctionTimeBase, "TIME BASE", 2, "TIME_BASE"); -virtual void OutputComputable(ContextInfo *info, ExpressionList *arg) override; -} -; - -FSubclassStart(FunctionDelayN, "DELAY N", 4, "DELAYN"); - -public: -virtual void OutputComputable(ContextInfo *info, ExpressionList *arg) override; -} -; -FSubclassStart(FunctionSmoothN, "SMOOTH N", 4, "SMTHN"); - -public: -virtual void OutputComputable(ContextInfo *info, ExpressionList *arg) override; -} -; - -FSubclassMemoryStart(FunctionInteg, "INTEG", 2, 0b10, 0b01, "integ_active", - "integ_init") virtual bool IsIntegrator() const override { - return true; -} -} -; -FSubclassMemoryStart(FunctionActiveInitial, "ACTIVE INITIAL", 2, 0b10, 0b01, "ai_active", - "ai_init") virtual bool IsActiveInit() override { - return true; -} -} -; -FSubclass(FunctionInitial, "INITIAL", 1, "INIT"); -FSubclass(FunctionReInitial, "REINITIAL", 1, "INIT"); - -FSubclassTime(FunctionRamp, "RAMP", 3, "RAMP"); -FSubclass(FunctionLn, "LN", 1, "LN"); -FSubclassTime(FunctionStep, "STEP", 2, "step"); - -// allocation -FSubclassStart(FunctionAllocateByPriority, "ALLOCATE BY PRIORITY", 5, "ALLOCATE") public - : virtual void OutputComputable(ContextInfo *info, ExpressionList *arg) override; -} -; - -FSubclassKeyword(FunctionTabbedArray, "TABBED ARRAY", 1); - -// functions that will never translate - but easier to catch error on other side -FSubclass(FunctionGetDirectData, "GET DIRECT DATA", 4, "GET_DIRECT_DATA"); -FSubclass(FunctionGetDataMean, "GET DATA MEAN", 3, "GET_DATA_MEAN"); - -/* -class FunctionMin : - public Function -{ -public : - FunctionMin(SymbolNameSpace *sns) : Function(sal,"MIN",2) { ; } - ~FunctionMin(void) { } - inline double Eval(Expression *,ExpressionList *arg) ; -} ; - -*/ - -class FunctionSampleIfTrue : public Function { -public: - FunctionSampleIfTrue(SymbolNameSpace *sns) : Function(sns, "SAMPLE IF TRUE", 3) { - } - ~FunctionSampleIfTrue(void) { - } - std::string ComputableName(void) override { - return "SampleIfTrue"; - } - virtual void OutputComputable(ContextInfo *info, ExpressionList *arg) override; - -private: -}; - -class FunctionPulse : public Function { -public: - FunctionPulse(SymbolNameSpace *sns) : Function(sns, "PULSE", 2) { - } - ~FunctionPulse(void) { - } - std::string ComputableName(void) override { - return "Pulse"; - } - virtual void OutputComputable(ContextInfo *info, ExpressionList *arg) override; - -private: -}; -class FunctionPulseTrain : public Function { -public: - FunctionPulseTrain(SymbolNameSpace *sns) : Function(sns, "PULSE TRAIN", 4) { - } - ~FunctionPulseTrain(void) { - } - std::string ComputableName(void) override { - return "PulseTrain"; - } - virtual void OutputComputable(ContextInfo *info, ExpressionList *arg) override; - -private: -}; -class FunctionQuantum : public Function { -public: - FunctionQuantum(SymbolNameSpace *sns) : Function(sns, "QUANTUM", 2) { - } - ~FunctionQuantum(void) { - } - std::string ComputableName(void) override { - return "Quantum"; - } - virtual void OutputComputable(ContextInfo *info, ExpressionList *arg) override; - -private: -}; -class FunctionRandomNormal : public Function { -public: - FunctionRandomNormal(SymbolNameSpace *sns) : Function(sns, "RANDOM NORMAL", 5) { - } - ~FunctionRandomNormal(void) { - } - std::string ComputableName(void) override { - return "RandomNormal"; - } - virtual void OutputComputable(ContextInfo *info, ExpressionList *arg) override; - -private: -}; - -class FunctionRandomPoisson : public Function { -public: - FunctionRandomPoisson(SymbolNameSpace *sns) : Function(sns, "RANDOM POISSON", 6) { - } - ~FunctionRandomPoisson(void) { - } - std::string ComputableName(void) override { - return "RandomPoisson"; - } - virtual void OutputComputable(ContextInfo *info, ExpressionList *arg) override; - -private: -}; - -class FunctionIfThenElse : public Function { -public: - FunctionIfThenElse(SymbolNameSpace *sns) : Function(sns, "IF THEN ELSE", 3) { - ; - } - ~FunctionIfThenElse(void) { - } - std::string ComputableName(void) override { - return "IF"; - } - virtual void OutputComputable(ContextInfo *info, ExpressionList *arg) override; - -private: -}; - -class FunctionLog : public Function { -public: - FunctionLog(SymbolNameSpace *sns) : Function(sns, "LOG", 2) { - ; - } - ~FunctionLog(void) { - } - std::string ComputableName(void) override { - return "LOG10"; - } - virtual void OutputComputable(ContextInfo *info, ExpressionList *arg) override; - -private: -}; -#endif diff --git a/src/xmutil/third_party/xmutil/Function/Level.cpp b/src/xmutil/third_party/xmutil/Function/Level.cpp deleted file mode 100644 index 50b233813..000000000 --- a/src/xmutil/third_party/xmutil/Function/Level.cpp +++ /dev/null @@ -1,7 +0,0 @@ -#include "Level.h" - -Level::Level(SymbolNameSpace *sns, const std::string &name, int nargs) : Function(sns, name, nargs) { -} - -Level::~Level(void) { -} diff --git a/src/xmutil/third_party/xmutil/Function/Level.h b/src/xmutil/third_party/xmutil/Function/Level.h deleted file mode 100644 index a52651521..000000000 --- a/src/xmutil/third_party/xmutil/Function/Level.h +++ /dev/null @@ -1,24 +0,0 @@ -#ifndef _XMUTIL_FUNCTION_LEVEL_H -#define _XMUTIL_FUNCTION_LEVEL_H - -/* Level is the abstract class for functions with states - it is subclassed from Function - but might also be treated - in parallel */ - -typedef struct _tag_state_info_ { - int size; - int n; -} lkjkljklj; - -#include "Function.h" -class Level : public Function { -public: - Level(SymbolNameSpace *sns, const std::string &name, int nargs); - virtual ~Level(void) = 0; - inline double EvalEq() { - return GetStateVal(); - } - virtual double GetStateVal() = 0; -}; - -#endif \ No newline at end of file diff --git a/src/xmutil/third_party/xmutil/Function/State.cpp b/src/xmutil/third_party/xmutil/Function/State.cpp deleted file mode 100644 index c0791093d..000000000 --- a/src/xmutil/third_party/xmutil/Function/State.cpp +++ /dev/null @@ -1,15 +0,0 @@ -#include "State.h" - -#include "../Symbol/Variable.h" -#include "../XMUtil.h" - -State::State(SymbolNameSpace *sns) : SymbolTableBase(sns) { - pVals = NULL; - iNVals = 0; - cComputeFlag = 0; - cDynamicDependencyFlag = 0; - cSparse = 0; -} - -State::~State(void) { -} diff --git a/src/xmutil/third_party/xmutil/Function/State.h b/src/xmutil/third_party/xmutil/Function/State.h deleted file mode 100644 index 3ef11ee6f..000000000 --- a/src/xmutil/third_party/xmutil/Function/State.h +++ /dev/null @@ -1,99 +0,0 @@ -#ifndef _XMUTIL_FUNCTION_STATE_H -#define _XMUTIL_FUNCTION_STATE_H - -#include - -#include - -#include "../Symbol/SymbolTableBase.h" - -class Variable; - -/* state - contains information about the value of either explicit - or implicit variables such as those associated with functions with - memory - the base, and most common, state is simply a vector of - numbers */ - -class State : public SymbolTableBase // just to handle memory cleanup when an exception is thrown -{ -public: - State(SymbolNameSpace *sns); - ~State(void); - friend class VariableContentVar; - virtual bool HasMemory(void) { - return false; - } // but true for levels - virtual bool UpdateOnPartialStep() { - return true; - } // but false for STEP... - inline int SetStateVec(double *p); - inline void ClearComputeFlag(void) { - cComputeFlag = 0; - } - inline double GetValue(int off) { - return pVals[off]; - } - inline void SetInitialValue(int off, double val) { - pVals[off] = val; - } - virtual void SetActiveValue(int off, double val) { - pVals[off] = val; - } - virtual void SetRateP(double *p) { - assert(0); - } - /* don't use these - levells override SetActiveValue which is for rates - during normal compuation - inline double & operator [](int off) { return pVals[off] ; } - inline double & Value(int off) { return pVals[off] ; } - */ -private: - double *pVals; // this will be allocated from a global pool to facilitate variable step size integration - int iNVals; // actual number in pVals - might be sparse for an array - unsigned short cComputeFlag; // used in equation ordering for unnamed state variables - unsigned char cDynamicDependencyFlag; // see context info - unsigned char cSparse; // special value lookup required -}; - -class StateLevel : public State { -public: - StateLevel(SymbolNameSpace *sns) : State(sns) { - } - ~StateLevel(void) { - } - bool HasMemory(void) { - return true; - } - void SetActiveValue(int off, double val) { - pRates[off] = val; - } - void SetRateP(double *p) { - pRates = p; - } - -private: - double *pRates; -}; - -class StateTime : public State { -public: - StateTime(SymbolNameSpace *sns) : State(sns) { - } - ~StateTime(void) { - } - bool UpdateOnPartialStep() { - return false; - } // but false for STEP... -private: -}; - -class StateSubscriptRange : public State { - StateSubscriptRange(SymbolNameSpace *sns) : State(sns) { - } - ~StateSubscriptRange(void) { - } - -private: -}; - -#endif diff --git a/src/xmutil/third_party/xmutil/Function/TableFunction.cpp b/src/xmutil/third_party/xmutil/Function/TableFunction.cpp deleted file mode 100644 index ac6634558..000000000 --- a/src/xmutil/third_party/xmutil/Function/TableFunction.cpp +++ /dev/null @@ -1,40 +0,0 @@ -#include "TableFunction.h" - -#include - -#include - -#include "../Symbol/Expression.h" -#include "../Symbol/Variable.h" -#include "../XMUtil.h" - -TableFunction::TableFunction(void) { -} - -TableFunction::~TableFunction(void) { -} - -double TableFunction::Eval(ExpressionVariable *v, Expression *e, ContextInfo *info) { - double d = e->Eval(info); - ExpressionTable *et; - - et = static_cast(v->GetVariable()->GetEquation(0)->GetExpression()); - assert(et->GetType() == EXPTYPE_Table); - double *x = et->GetXVals()->data(); - double *y = et->GetYVals()->data(); - int n = et->GetXVals()->size(); - assert(n == et->GetYVals()->size()); - if (d <= x[0]) - return y[0]; - if (d >= x[n - 1]) - return y[n - 1]; - for (int i = 0; i < n; i++) { - if (d >= x[i]) { - while (d >= x[i + 1]) - i++; // in case - return y[i] + (y[i + 1] - y[i]) * (d - x[i]) / (x[i + 1] - x[i]); - } - } - assert(0); - return FLT_MAX; -} \ No newline at end of file diff --git a/src/xmutil/third_party/xmutil/Function/TableFunction.h b/src/xmutil/third_party/xmutil/Function/TableFunction.h deleted file mode 100644 index 99c8b5039..000000000 --- a/src/xmutil/third_party/xmutil/Function/TableFunction.h +++ /dev/null @@ -1,21 +0,0 @@ -#ifndef _XMUTIL_TABLEFUNCTION_H -#define _XMUTIL_TABLEFUNCTION_H - -class Expression; -class ExpressionVariable; -class ContextInfo; - -/* table or lookup functions are used implicitly and are therefore not derived from - Function (this it is called TableFunction not Function Table) - nor is it derived from State - - first pass this is never instantiated - only eval is ever called - */ -class TableFunction { -public: - TableFunction(void); - ~TableFunction(void); - static double Eval(ExpressionVariable *v, Expression *e, ContextInfo *info); -}; - -#endif diff --git a/src/xmutil/third_party/xmutil/LICENSE b/src/xmutil/third_party/xmutil/LICENSE deleted file mode 100644 index 491594b08..000000000 --- a/src/xmutil/third_party/xmutil/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2018 bobeberlein - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/src/xmutil/third_party/xmutil/Log.cpp b/src/xmutil/third_party/xmutil/Log.cpp deleted file mode 100644 index 0f1848870..000000000 --- a/src/xmutil/third_party/xmutil/Log.cpp +++ /dev/null @@ -1,39 +0,0 @@ -#include "Log.h" - -#include -#include -#include -#include - -#if defined WIN32 || defined __APPLE__ -#define ATTRIBUTE_PRINTF -#else -// this gives us better compiler error messages for callers -#define ATTRIBUTE_PRINTF __attribute__((format(printf, 1, 2))) -#endif - -static std::string global_log; - -void ATTRIBUTE_PRINTF log(const char *msg_fmt, ...) { - char *msg = nullptr; - va_list args; - int ret; - - va_start(args, msg_fmt); - ret = vasprintf(&msg, msg_fmt, args); - va_end(args); - - if (ret != -1 && msg != nullptr) { - global_log += msg; - free(msg); - } -} - -extern "C" { -const char *xmutil_get_log(void) { - return global_log.c_str(); -} -void xmutil_clear_log(void) { - global_log = ""; -} -} diff --git a/src/xmutil/third_party/xmutil/Log.h b/src/xmutil/third_party/xmutil/Log.h deleted file mode 100644 index b68411dae..000000000 --- a/src/xmutil/third_party/xmutil/Log.h +++ /dev/null @@ -1,10 +0,0 @@ -// Log.h - functions for emitting logging information - wraps writing to stderr. -#ifndef _XMUTIL_LOG_H -#define _XMUTIL_LOG_H - -// for use by bison-generated parser -#define XmutilLogf(file, msgFmt, args...) log(msgFmt, ##args) - -void log(const char *msgFmt, ...); - -#endif diff --git a/src/xmutil/third_party/xmutil/Model.cpp b/src/xmutil/third_party/xmutil/Model.cpp deleted file mode 100644 index 4b9b1b470..000000000 --- a/src/xmutil/third_party/xmutil/Model.cpp +++ /dev/null @@ -1,694 +0,0 @@ -#include "Model.h" - -#include - -#include "Symbol/Equation.h" -#include "Symbol/LeftHandSide.h" -#include "Symbol/Symbol.h" -#include "Vensim/VensimView.h" -#include "XMUtil.h" -#include "Xmile/XMILEGenerator.h" - -Model::Model(void) { - dLevel = dRate = dAux = NULL; - bAsSectors = false; - iIntegrationType = Integration_Type_EULER; - bLetterPolarity = false; - _initial_time = -1; - _final_time = 200; - _dt = 1; - bFromDyanmo = false; -} - -Model::~Model(void) { - // Clean up dynamically allocated views - for (View *view : vViews) { - delete view; - } - vViews.clear(); - - // Clean up dynamically allocated groups - for (ModelGroup *group : vGroups) { - delete group; - } - vGroups.clear(); - - // Clean up dynamically allocated macro functions. MacroFunctions - // are registered in mSymbolNameSpace by the Symbol constructor, so - // we must remove them before deleting to avoid a double-free when - // mSymbolNameSpace's destructor also tries to delete them. - for (MacroFunction *func : mMacroFunctions) { - mSymbolNameSpace.Remove(func); - delete func; - } - mMacroFunctions.clear(); - - // Clean up unnamed variables - for (Variable *var : vUnamedVars) { - delete var; - } - vUnamedVars.clear(); - - // Clean up any allocated arrays - if (dLevel) { - delete[] dLevel; - dLevel = NULL; - } - if (dRate) { - delete[] dRate; - dRate = NULL; - } - if (dAux) { - delete[] dAux; - dAux = NULL; - } -} - -Equation *Model::AddUnnamedVariable(ExpressionFunctionMemory *e) { - ExpressionFunction *e2; - std::string s; - assert(s.empty()); - Variable *var = new Variable(&mSymbolNameSpace, s); - ExpressionVariable *ev = new ExpressionVariable(&mSymbolNameSpace, var, NULL); - LeftHandSide *lhs = new LeftHandSide(&mSymbolNameSpace, ev, NULL, NULL, 0); - e2 = new ExpressionFunction(&mSymbolNameSpace, e->GetFunction(), e->GetArgs()); - Equation *eq = new Equation(&mSymbolNameSpace, lhs, e2, '='); - // log("Adding in a placeholder from function %s\n",e->GetFunction()->GetName().c_str()) ; - var->AddEq(eq); - vUnamedVars.push_back(var); - return eq; -} - -void Model::ClearCompEquations(void) { - vActiveComps.clear(); - vInitialComps.clear(); - vUnchangingComps.clear(); - vInitialTimeComps.clear(); - - SymbolNameSpace::HashTable *ht = mSymbolNameSpace.GetHashTable(); - for (const SymbolNameSpace::iterator &it : *ht) { - SNSitToSymbol(it)->SetupState(NULL); - SNSitToSymbol(it)->CheckPlaceholderVars(NULL); - } - for (Variable *v : vUnamedVars) { - v->GetEquation(0)->GetExpression()->RemoveFunctionArgs(); // these are allocated in the real variable's equation - v->SetupState(NULL); - delete v; - } - if (dLevel) { - delete[] dLevel; - dLevel = NULL; - } - if (dRate) { - delete[] dRate; - dRate = NULL; - } - if (dAux) { - delete[] dAux; - dAux = NULL; - } -} -typedef struct { - Variable *v; - int count; -} SubInfoWCount; -bool Model::OrganizeSubscripts(void) { - std::vector sublist; - std::vector subelm; - SubInfoWCount siwc; - try { - SymbolNameSpace::HashTable *ht = mSymbolNameSpace.GetHashTable(); - for (const SymbolNameSpace::iterator &it : *ht) { - siwc.v = static_cast SNSitToSymbol(it); - siwc.count = siwc.v->SubscriptCountVars(subelm); - if (siwc.count > 0) { - sublist.push_back(siwc); - } - } - mSymbolNameSpace.ConfirmAllAllocations(); - } catch (...) { - mSymbolNameSpace.DeleteAllUnconfirmedAllocations(); - return false; - } - return true; -} - -bool Model::ValidatePlaceholderVars(void) { - try { - SymbolNameSpace::HashTable *ht = mSymbolNameSpace.GetHashTable(); - for (const SymbolNameSpace::iterator &it : *ht) { - // log("Checking placeholders out %s\n",SNSitToSymbol(it)->GetName().c_str()) ; - SNSitToSymbol(it)->CheckPlaceholderVars(this); - } - mSymbolNameSpace.ConfirmAllAllocations(); - } catch (...) { - mSymbolNameSpace.DeleteAllUnconfirmedAllocations(); - return false; - } - return true; -} - -bool Model::SetupVariableStates(int pass /* 0 just assign, 1 determine sizes, 2 pass pointers for computation*/) { - ContextInfo info(NULL); - info.pSymbolNameSpace = &mSymbolNameSpace; - SymbolNameSpace::HashTable *ht = mSymbolNameSpace.GetHashTable(); - info.iComputeType = pass; // flag to skip empty or count sizes - try { - if (pass == 2) { - if (iNLevel) { - dLevel = new double[iNLevel]; - dRate = new double[iNLevel]; - } else - dLevel = dRate = NULL; - if (iNAux) - dAux = new double[iNAux]; - else - dAux = NULL; - info.pBaseAux = info.pCurAux = dAux; - info.pBaseRate = info.pCurRate = dRate; - info.pBaseLevel = info.pCurLevel = dLevel; - } else { - info.pBaseAux = info.pCurAux = NULL; - info.pBaseRate = info.pCurRate = NULL; - info.pBaseLevel = info.pCurLevel = NULL; - info.iComputeType = 0; - } - for (const SymbolNameSpace::iterator &it : *ht) { - SNSitToSymbol(it)->SetupState(&info); - } - // placeholder vars also need state set up - for (Variable *v : vUnamedVars) { - v->SetupState(&info); - } - mSymbolNameSpace.ConfirmAllAllocations(); - if (pass == 1) { - iNLevel = (info.pCurLevel - info.pBaseLevel); - iNAux = (info.pCurAux - info.pBaseAux); - } - } catch (...) { - // set all states to null - they will be deleted - for (const SymbolNameSpace::iterator &it : *ht) { - SNSitToSymbol(it)->SetupState(NULL); // clear if setup - } - mSymbolNameSpace.DeleteAllUnconfirmedAllocations(); - if (dLevel) { - delete dLevel; - dLevel = NULL; - } - if (dRate) { - delete dRate; - dRate = NULL; - } - if (dAux) { - delete dAux; - dAux = NULL; - } - return false; - } - return true; -} - -/* start anywhere - we just use the iterator order on the hash table - - and get every variable computed - this needs to be done for both - active and initial value (potentially reinitial as well but that is - left out for now). - */ -bool Model::OrderEquations(ContextInfo *info, bool tonly) { - bool haserr = false; - try { - SymbolNameSpace::HashTable *ht = mSymbolNameSpace.GetHashTable(); - if (tonly) { - Variable *v; - v = static_cast(mSymbolNameSpace.Find("INITIAL TIME")); - // note CheckComputed called with false for first otherwise these won't be - // initialized - if (!v || !v->CheckComputed(info, false)) - haserr = true; - v = static_cast(mSymbolNameSpace.Find("TIME STEP")); - if (!v || !v->CheckComputed(info, false)) - haserr = true; - } else { - for (const SymbolNameSpace::iterator &it : *ht) { - // log("Looping to: %s\n",SNSitToSymbol(it)->GetName().c_str()) ; - if (!SNSitToSymbol(it)->CheckComputed(info, true)) - haserr = true; // continue looking for simultaneous even when false - } - for (Variable *v : vUnamedVars) { - if (!v->CheckComputed(info, true)) - haserr = true; - } - } - - mSymbolNameSpace.ConfirmAllAllocations(); - } catch (...) { - mSymbolNameSpace.DeleteAllUnconfirmedAllocations(); - return false; - } - return !haserr; -} - -bool Model::AnalyzeEquations(void) { - ContextInfo info(NULL); - - ClearCompEquations(); // will also reset comp flag and delete placeholder vars - /* ValidatePlaceholderVars will create the variables required for functions that either - have memory are are time dependent (and therefore not subject to change within - a DT as used in all but Euler integration */ - if (!ValidatePlaceholderVars()) - return false; - /* SetupVariableStates will create states based on the variable equation types - including subscript states needed to organize subscripts */ - if (!SetupVariableStates(0)) - return false; - if (!OrganizeSubscripts()) - return false; - if (!SetupVariableStates(1)) /* get size count */ - return false; - if (!SetupVariableStates(2)) /* allocate and assign computed value locations */ - return false; - // three passes on ordering equations first for initial - // then for active - but putting out only those equations - // that are not clearly unchanging - then again for - // unchanging which are equations not put out in step - // 2 then during execution 1 and 3 are computed once - // and 2 is cycled through for integration - // - - // before the passes initialize time, then dt - log("\nInitial time \n"); - info.iComputeType = CF_initial; - info.pEquations = &vInitialTimeComps; - if (!OrderEquations(&info, true)) - return false; - - log("\nInitial equations \n"); - info.iComputeType = CF_initial; - info.pEquations = &vInitialComps; - if (!OrderEquations(&info, false)) - return false; - log("\n\nActive equations \n"); - info.iComputeType = CF_active; - info.pEquations = &vActiveComps; - if (!OrderEquations(&info, false)) - return false; - log("\n\nUnchanging equations \n"); - info.iComputeType = CF_unchanging; - info.pEquations = &vUnchangingComps; - if (!OrderEquations(&info, false)) - return false; - log("\n\nRate equations \n"); - info.iComputeType = CF_rate; - info.pEquations = &vRateComps; - if (!OrderEquations(&info, false)) - return false; - return true; -} - -bool Model::Simulate(void) { - ContextInfo info(NULL); - try { - double t, s, e, dt; - int i, n; - Variable *time = static_cast(mSymbolNameSpace.Find("Time")); - Variable *start = static_cast(mSymbolNameSpace.Find("INITIAL TIME")); - Variable *end = static_cast(mSymbolNameSpace.Find("FINAL TIME")); - Variable *step = static_cast(mSymbolNameSpace.Find("TIME STEP")); - n = iNLevel; - - info.iComputeType = CF_initial; - for (Equation *e : vInitialTimeComps) { - e->Execute(&info); - // log("%s = %g\n",e->GetVariable()->GetName().c_str(),e->GetVariable()->Eval(&info)) ; - } - if (start) - s = start->Eval(&info); - else - s = 0; - if (step) - dt = step->Eval(&info); - else - dt = 1; - info.dTime = s; - info.dDT = dt; - for (Equation *e : vInitialComps) { - e->Execute(&info); - // log("%s = %g\n",e->GetVariable()->GetName().c_str(),e->GetVariable()->Eval(&info)) ; - } - // now the active equations - info.iComputeType = CF_active; - // first the unchanging variables - // log("\n Unchanging\n") ; - for (Equation *e : vUnchangingComps) { - e->Execute(&info); - // log("%s = %g\n",e->GetVariable()->GetName().c_str(),e->GetVariable()->Eval(&info)) ; - } - - // now over time - if (end) - e = end->Eval(&info); - else - e = 100; - log("Time"); - for (Equation *e : vActiveComps) { - log("\t%s", e->GetVariable()->GetName().c_str()); - }; - for (Equation *e : vRateComps) { - log("\t%s", e->GetVariable()->GetName().c_str()); - } - log("\n"); - - for (t = s; t <= e; t += dt) { - info.dTime = t; - // log("\n\nAt time %g\n",t) ; - if (time) - time->SetActiveValue(0, t); - log("%g", t); - for (Equation *e : vActiveComps) { - e->Execute(&info); - log("\t%g", e->GetVariable()->Eval(&info)); - } - for (Equation *e : vRateComps) { - e->Execute(&info); - log("\t%g", e->GetVariable()->Eval(&info)); - } - log("\n"); - if (step) - info.dDT = dt = step->Eval(&info); - // update states - for (i = 0; i < n; i++) { - dLevel[i] += dt * this->dRate[i]; - } - if (end) - e = end->Eval(&info); - } - } catch (...) { - log("Error of some sort"); - return false; - } - return true; -} - -bool Model::OutputComputable(bool wantshort) { - ContextInfo info(NULL); - try { - if (wantshort) - GenerateShortNames(); - else - GenerateCanonicalNames(); - info.iComputeType = CF_initial; - log("------------- initial time -----------------\n"); - for (Equation *e : vInitialTimeComps) { - e->OutputComputable(&info); - } - log("------------- initialization -----------------\n"); - for (Equation *e : vInitialComps) { - e->OutputComputable(&info); - } - info.iComputeType = CF_active; - log("------------- Unchanging -----------------\n"); - info.iComputeType = CF_active; - for (Equation *e : vUnchangingComps) { - e->OutputComputable(&info); - } - log("------------- active -----------------\n"); - for (Equation *e : vActiveComps) { - e->OutputComputable(&info); - } - info.iComputeType = CF_rate; - log("------------- rates -----------------\n"); - for (Equation *e : vRateComps) { - e->OutputComputable(&info); - } - } catch (...) { - log("Error of some sort"); - return false; - } - return true; -} - -bool Model::MarkVariableTypes(SymbolNameSpace *ns) { - try { - SymbolNameSpace::HashTable *ht; - if (ns) - ht = ns->GetHashTable(); - else { - ht = mSymbolNameSpace.GetHashTable(); - ns = &mSymbolNameSpace; - } - std::vector vars; - for (const SymbolNameSpace::iterator &it : *ht) { - Symbol *sym = SNSitToSymbol(it); - - if (sym->isType() == Symtype_Variable) - vars.push_back(static_cast(sym)); - } - // - for (Variable *var : vars) { - var->PurgeAFOEq(); - var->MarkTypes(ns); // may change number of entries so can't be in above loop - } - // repeat this for flows after all stocks marked - for (Variable *var : vars) { - var->MarkStockFlows(ns); // may change number of entries so can't be in above loop - } - // don't do this - we have broken the allocation setup mSymbolNameSpace.ConfirmAllAllocations(); - } catch (...) { - ns->DeleteAllUnconfirmedAllocations(); - return false; - } - -#ifdef dodump - // - dump eveything - mostly just to see how the translation is going - ContextInfo info; - - SymbolNameSpace::HashTable *ht = mSymbolNameSpace.GetHashTable(); - for (const SymbolNameSpace::iterator &it : *ht) { - Symbol *sym = SNSitToSymbol(it); - - if (sym->isType() == Symtype_Variable) { - Variable *var = static_cast(sym); - VariableContent *content = var->Content(); - if (content) { // array elements don't have - for (Equation *eq : content->GetAllEquations()) { - eq->OutputComputable(&info); - } - } - } - } -#endif - - return true; -} - -void Model::AdjustGroupNames() { - // every group name must be unique and can't be in the global name space - int n = vGroups.size(); - for (int i = 0; i < n; i++) { - std::string name = vGroups[i]->sName; - // test it - bool good; - do { - good = mSymbolNameSpace.Find(name) == NULL; - if (good) { - std::string *lname = SymbolNameSpace::ToLowerSpace(name); - for (int j = 0; good && j < i; j++) { - std::string *tlname = SymbolNameSpace::ToLowerSpace(vGroups[j]->sName); - if (*tlname == *lname) - good = false; - delete tlname; - } - delete lname; - } - if (!good) - name += " 1"; - } while (!good); - vGroups[i]->sName = name; - } -} - -void Model::CheckGhostOwners() { - // now everything is defined (and only defined once) - we need to make sure there are no missing connectors - for (View *view : vViews) { - view->CheckGhostOwners(); - } -} - -void Model::AttachStragglers() { - SymbolNameSpace::HashTable *ht = mSymbolNameSpace.GetHashTable(); - std::vector vars; - for (const SymbolNameSpace::iterator &it : *ht) { - Symbol *sym = SNSitToSymbol(it); - - if (sym->isType() == Symtype_Variable) - vars.push_back(static_cast(sym)); - } - // first try - anything that is not defined somewhere see if a ghost appears somewhere - // and change that to the definition - for (Variable *var : vars) { - if (!var->GetView()) { - for (View *view : vViews) { - if (view->UpgradeGhost(var)) - break; - } - } - } - // now try undefined flows - attach to associated stocks - for (Variable *var : vars) { - if (!var->GetView() && var->VariableType() == XMILE_Type_FLOW) { - // we don't know flows uses so just look at all stocks - Variable *upstream = NULL; - Variable *downstream = NULL; - for (Variable *stock : vars) { - if (stock->VariableType() == XMILE_Type_STOCK) { - for (Variable *in : stock->Inflows()) { - if (in == var) { - downstream = stock; - break; - } - } - for (Variable *out : stock->Outflows()) { - if (out == var) { - upstream = stock; - break; - } - } - if (upstream && downstream) - break; - } - } - if (upstream && upstream->GetView()) { - upstream->GetView()->AddFlowDefinition(var, upstream, downstream); - } else if (downstream && downstream->GetView()) { - downstream->GetView()->AddFlowDefinition(var, upstream, downstream); - } - } - } - // next pass look for inputs - if there are any put the var next to them - // next pass look for otuputs - if there are any put the var next to them - // finally dump everything remaining at 40,40 on the first view - if (!vViews.empty()) { - View *dump_view = vViews[0]; - for (Variable *var : vars) { - if (!var->GetView() && var->VariableType() != XMILE_Type_ARRAY && var->VariableType() != XMILE_Type_ARRAY_ELM && - var->VariableType() != XMILE_Type_UNKNOWN) - dump_view->AddVarDefinition(var, 200, 200); - } - } - - // now everything is defined (and only defined once) - we need to make sure there are no missing connectors - for (View *view : vViews) { - view->CheckLinksIn(); - } -} - -void Model::MakeViewNamesUnique() { - std::vector &views = Views(); - std::set names; - for (View *gview : views) { - VensimView *view = static_cast(gview); - std::string name; - // get rid of . and - in the name - for (char c : view->Title()) { - if (c == '.' || c == '-' || c == '+' || c == ',' || c == '/' || c == '*' || c == '^') - c = ' '; - if (c != ' ' || (!name.empty() && name.back() != ' ')) - name.push_back(c); - } - if (name.empty()) - name = "Module "; - while (GetNameSpace()->Find(name) || names.find(name) != names.end()) { - name += "1"; // not very original - } - names.insert(name); - view->SetTitle(name); - } -} - -bool Model::RenameVariable(Variable *v, const std::string &newname) { - assert(!newname.empty()); - if (mSymbolNameSpace.Find(newname)) { - if (!newname.compare(v->GetName())) - return true; // nothing to do - return false; - } - if (!mSymbolNameSpace.Remove(v)) - return false; - v->SetName(newname); - mSymbolNameSpace.Insert(v); - return true; -} - -void Model::GenerateCanonicalNames(void) { - assert(0); -} - -void Model::GenerateShortNames(void) { - int i = 0; - SymbolNameSpace::HashTable *ht = mSymbolNameSpace.GetHashTable(); - for (const SymbolNameSpace::iterator &it : *ht) { - Variable *v = static_cast SNSitToSymbol(it); - if (v->isType() == Symtype_Variable) { - std::string s = "v" + std::to_string(i); - i++; - v->SetAlternateName(s); - // v->SetAlternateName(v->GetName()) ; - } - } - for (Variable *v : vUnamedVars) { - std::string s = "v" + std::to_string(i); - i++; - v->SetAlternateName(s); - } -} - -double Model::GetConstanValue(const char *str, double val) { - Symbol *s = mSymbolNameSpace.Find(str); - if (s && s->isType() == Symtype_Variable) { - Variable *v = static_cast(s); - Equation *eq = v->GetEquation(0); - if (eq) { - Expression *exp = eq->GetExpression(); - if (exp && exp->GetType() == EXPTYPE_Number) - val = exp->Eval(NULL); - } - } - return val; -} - -UnitExpression *Model::GetUnits(const char *str) { - Symbol *s = mSymbolNameSpace.Find(str); - if (s && s->isType() == Symtype_Variable) { - Variable *v = static_cast(s); - return v->Units(); - } - return NULL; -} - -void Model::SetUnwanted(const char *str, const char *defname) { - Symbol *s = mSymbolNameSpace.Find(str); - if (s && s->isType() == Symtype_Variable) { - Variable *v = static_cast(s); - v->SetUnwanted(true); - v->SetAlternateName(defname); - } -} - -std::vector Model::GetVariables(SymbolNameSpace *ns) { - std::vector vars; - SymbolNameSpace::HashTable *ht; - if (ns) - ht = ns->GetHashTable(); - else - ht = mSymbolNameSpace.GetHashTable(); - for (auto it = ht->begin(); it != ht->end(); it++) { - Symbol *s = it->second; - if (s->isType() == Symtype_Variable) - vars.push_back(static_cast(s)); - } - return vars; -} - -std::string Model::PrintXMILE(bool isCompact, std::vector &errs, double xscale, double yscale) { - XMILEGenerator generator(this, xscale, yscale, bFromDyanmo); - return generator.Print(isCompact, errs, bAsSectors); -} diff --git a/src/xmutil/third_party/xmutil/Model.h b/src/xmutil/third_party/xmutil/Model.h deleted file mode 100644 index 99caf9a59..000000000 --- a/src/xmutil/third_party/xmutil/Model.h +++ /dev/null @@ -1,145 +0,0 @@ -#ifndef _XMUTIL_MODEL_H -#define _XMUTIL_MODEL_H -#include - -#include "Symbol/Expression.h" -#include "Symbol/Variable.h" - -enum Integration_Type { Integration_Type_EULER, Integration_Type_RK2, Integration_Type_RK4 }; -class View { -public: - virtual ~View() = default; // Virtual destructor for proper cleanup of derived classes - virtual bool UpgradeGhost(Variable *var) = 0; - virtual bool AddFlowDefinition(Variable *var, Variable *in, Variable *out) = 0; - virtual bool AddVarDefinition(Variable *var, int x, int y) = 0; - virtual void CheckLinksIn() = 0; - virtual void CheckGhostOwners() = 0; - virtual bool empty() const = 0; - // just a placeholder to derive from -}; -class Model { -public: - Model(void); - ~Model(void); - bool UnitsCheck(void) { - return false; - } - bool AnalyzeEquations(void); - bool Simulate(void); - SymbolNameSpace *GetNameSpace(void) { - return &mSymbolNameSpace; - } - Equation *AddUnnamedVariable(ExpressionFunctionMemory *e); - bool RenameVariable(Variable *v, const std::string &newname); - void GenerateCanonicalNames(void); - void GenerateShortNames(void); - bool OutputComputable(bool wantshort); - bool MarkVariableTypes(SymbolNameSpace *ns); - void AdjustGroupNames(); - void CheckGhostOwners(); - void AttachStragglers(); // try to get diagramatic stuff right - void MakeViewNamesUnique(); - std::string PrintXMILE(bool isCompact, std::vector &errs, double xscale, double yscale); - - double GetConstanValue(const char *var, double defval); - UnitExpression *GetUnits(const char *var); - std::vector &UnitEquivs() { - return vUnitEquivs; - } - void SetUnwanted(const char *var, const char *nametouse); - std::vector GetVariables(SymbolNameSpace *ns = NULL); - void AddView(View *view) { - vViews.push_back(view); - } - std::vector &Views() { - return vViews; - } - - std::vector &MacroFunctions() { - return mMacroFunctions; - } - void SetMacroFunctions(std::vector set) { - mMacroFunctions = set; - } - void SetIntegrationType(Integration_Type type) { - iIntegrationType = type; - } - Integration_Type IntegrationType() { - return iIntegrationType; - } - std::vector &Groups() { - return vGroups; - } - - void SetAsSectors(bool set) { - bAsSectors = set; - } - bool AsSectors() const { - return bAsSectors; - } - - void SetLetterPolarity(bool set) { - bLetterPolarity = set; - } - bool LetterPolarity() const { - return bLetterPolarity; - } - - double initial_time() const { - return _initial_time; - } - void set_initial_time(double set) { - _initial_time = set; - } - double final_time() const { - return _final_time; - } - void set_finall_time(double set) { - _final_time = set; - } - double dt() const { - return _dt; - } - void set_dt(double set) { - _dt = set; - } - void set_from_dynamo(bool set) { - bFromDyanmo = set; - } - -private: - bool OrderEquations(ContextInfo *info, bool tonly); - bool SetupVariableStates(int pass); - bool ValidatePlaceholderVars(void); - bool OrganizeSubscripts(void); - void ClearCompEquations(void); - - SymbolNameSpace mSymbolNameSpace; - std::vector vGroups; - std::vector vViews; - std::vector vUnamedVars; - // std::vectorvConstantComps ; // actually just assignment - std::vector vInitialTimeComps; - std::vector vInitialComps; - std::vector vUnchangingComps; - std::vector vActiveComps; - std::vector vRateComps; - std::vector mMacroFunctions; - std::vector vUnitEquivs; - /* the last could be part of active but it is helpful to split - out when creating equations for a computer language */ - double _initial_time; - double _final_time; - double _dt; - int iNLevel; - int iNAux; - Integration_Type iIntegrationType; - double *dLevel; - double *dRate; - double *dAux; - bool bAsSectors; - bool bLetterPolarity; - bool bFromDyanmo; -}; - -#endif diff --git a/src/xmutil/third_party/xmutil/Symbol/Equation.cpp b/src/xmutil/third_party/xmutil/Symbol/Equation.cpp deleted file mode 100644 index ca2d95c2d..000000000 --- a/src/xmutil/third_party/xmutil/Symbol/Equation.cpp +++ /dev/null @@ -1,176 +0,0 @@ -#include "Equation.h" - -#include "../Model.h" -#include "../XMUtil.h" -#include "LeftHandSide.h" - -Equation::Equation(SymbolNameSpace *sns, LeftHandSide *lhs, Expression *ex, int tok) : SymbolTableBase(sns) { - pLeftHandSide = lhs; - pExpression = ex; - iEqType = tok; -} - -Equation::~Equation(void) { - if (HasGoodAlloc()) { - delete pLeftHandSide; - delete pExpression; - } -} - -Variable *Equation::GetVariable(void) { - return pLeftHandSide->GetVariable(); -} - -ExpressionTable *Equation::GetTable(void) { - return pExpression ? pExpression->GetTable() : NULL; -} - -void Equation::CheckPlaceholderVars(Model *m) { - if (pExpression) - pExpression->CheckPlaceholderVars(m, true); -} - -std::string Equation::RHSFormattedXMILE(Variable *lhs, const std::vector &subs, - const std::vector &dims, bool init) { - if (!pExpression) - return "{empty}"; - ContextInfo info(lhs); - if (init) - info.SetInitEqn(true); - - assert(subs.size() == dims.size()); - info.SetLHSElms(&subs, &dims); - pExpression->OutputComputable(&info); - return info.str(); -} - -bool Equation::IsActiveInit() { - return pExpression && pExpression->IsActiveInit(); -} - -void Equation::GetSubscriptElements(std::vector &vals, Symbol *s) { - assert(s->isType() == Symtype_Variable); - if (s->isType() != Symtype_Variable) - return; - Variable *v = static_cast(s); - - std::vector eqs = v->GetAllEquations(); - if (!eqs.empty()) // recur this is a nested def or equivalence - { - assert(eqs.size() == 1); - Expression *exp = eqs[0]->GetExpression(); - if (exp->GetType() == EXPTYPE_Variable) { - // equivalent - just make the right hand side the same - v = static_cast(exp)->GetVariable(); - eqs = v->GetAllEquations(); - assert(eqs.size() == 1); - exp = eqs[0]->GetExpression(); - } - assert(exp->GetType() == EXPTYPE_Symlist); - if (exp->GetType() == EXPTYPE_Symlist) { - SymbolList *symlist = static_cast(exp)->SymList(); - int n = symlist->Length(); - for (int i = 0; i < n; i++) { - const SymbolList::SymbolListEntry &elm = (*symlist)[i]; - if (elm.eType == SymbolList::EntryType_SYMBOL) // only valid type - GetSubscriptElements(vals, elm.u.pSymbol); - } - } - } else // otherwise the symbol is it - vals.push_back(s); -} - -// look at the lhs of equation to get element by element expanded subscripts - eg [plant] becomes [p1],[p2],[p3] -bool Equation::SubscriptExpand(std::vector> &elms, - std::vector &orig) // can be one or many depending on the subs -{ - SymbolList *subs = pLeftHandSide->GetSubs(); - if (!subs) - return false; - int n = subs->Length(); - std::vector> elmlist; - std::vector maxpos; - std::vector curpos; - std::vector cur_elms; - orig.clear(); - for (int i = 0; i < n; i++) { - cur_elms.clear(); - const SymbolList::SymbolListEntry &sub = (*subs)[i]; - if (sub.eType == SymbolList::EntryType_SYMBOL) // only valid type - { - // see if it has an equation - GetSubscriptElements(cur_elms, sub.u.pSymbol); - orig.push_back(sub.u.pSymbol); - } else { - assert(false); - cur_elms.push_back(NULL); - } - assert(!cur_elms.empty()); - elmlist.push_back(cur_elms); - maxpos.push_back(cur_elms.size()); - curpos.push_back(0); - } - // now cycle through elmlist - might be a single entry - we need to do all combinations - while (curpos[0] < maxpos[0]) { - cur_elms.clear(); - for (int i = 0; i < n; i++) - cur_elms.push_back(elmlist[i][curpos[i]]); - elms.push_back(cur_elms); - for (int j = n; j-- > 0;) { - curpos[j]++; - if (curpos[j] < maxpos[j]) - break; - if (j) - curpos[j] = 0; - } - } - - // elms.push_back(elmlist); - return n > 0; -} - -void Equation::Execute(ContextInfo *info) { - // info->LHS(pLeftHandSide) ; - if (info->GetComputeType() == CF_initial) - pLeftHandSide->GetVariable()->SetInitialValue(0, pExpression->Eval(info)); - else - pLeftHandSide->GetVariable()->SetActiveValue(0, pExpression->Eval(info)); -} -void Equation::OutputComputable(ContextInfo *info) { - *info << pLeftHandSide->GetVariable()->GetAlternateName(); - // subscripts - if (pLeftHandSide->GetSubs()) - pLeftHandSide->GetSubs()->OutputComputable(info); - *info << "="; - if (info->GetComputeType() == CF_rate) - *info << pLeftHandSide->GetVariable()->GetAlternateName() << "+dt*("; - if (pExpression) - pExpression->OutputComputable(info); - if (info->GetComputeType() == CF_rate) - *info << ")"; - *info << "\n"; -} - -void Equation::GetVarsUsed(std::vector &vars) { - if (pExpression) - pExpression->GetVarsUsed(vars); -} - -int Equation::SubscriptCount(std::vector &elmlist) { - if (iEqType == ':') /* a subscript equation */ - return 0; - // we just need to look at the LHS variable and count the number of subscripts on it - all usage should be the same - LeftHandSide *lhs = this->GetLeft(); - if (!lhs) - return 0; - SymbolList *subs = lhs->GetSubs(); - if (!subs) - return 0; - int n = subs->Length(); - for (int i = 0; i < n; i++) { - const SymbolList::SymbolListEntry &sub = (*subs)[i]; - if (sub.eType == SymbolList::EntryType_SYMBOL) // only valid type - elmlist.push_back(static_cast(sub.u.pSymbol)); - } - return n; -} diff --git a/src/xmutil/third_party/xmutil/Symbol/Equation.h b/src/xmutil/third_party/xmutil/Symbol/Equation.h deleted file mode 100644 index b7d3c9634..000000000 --- a/src/xmutil/third_party/xmutil/Symbol/Equation.h +++ /dev/null @@ -1,38 +0,0 @@ -#ifndef _XMUTIL_SYMBOL_EQUATION_H -#define _XMUTIL_SYMBOL_EQUATION_H -#include "../ContextInfo.h" -#include "SymbolTableBase.h" - -class Model; -class ExpressionTable; - -class Equation : public SymbolTableBase { -public: - Equation(SymbolNameSpace *sns, LeftHandSide *lhs, Expression *ex, int token /* = ( dataequals not standard */); - ~Equation(void); - LeftHandSide *GetLeft(void) { - return pLeftHandSide; - } - Variable *GetVariable(void); // LeftHandSide forward declared can't put function here - void GetVarsUsed(std::vector &vars); - inline Expression *GetExpression(void) { - return pExpression; - } - ExpressionTable *GetTable(void); - int SubscriptCount(std::vector &elmlist); - static void GetSubscriptElements(std::vector &vals, Symbol *s); // if nested defs - bool SubscriptExpand(std::vector> &elms, - std::vector &subs); // can be one or many depending on the subs - void Execute(ContextInfo *info); - void OutputComputable(ContextInfo *info); - void CheckPlaceholderVars(Model *m); - std::string RHSFormattedXMILE(Variable *lhs, const std::vector &subs, const std::vector &dims, - bool init); // need a_b*c - bool IsActiveInit(); // true only for active -private: - LeftHandSide *pLeftHandSide; - Expression *pExpression; - int iEqType; -}; - -#endif \ No newline at end of file diff --git a/src/xmutil/third_party/xmutil/Symbol/Expression.cpp b/src/xmutil/third_party/xmutil/Symbol/Expression.cpp deleted file mode 100644 index e19497c52..000000000 --- a/src/xmutil/third_party/xmutil/Symbol/Expression.cpp +++ /dev/null @@ -1,264 +0,0 @@ -#include "Expression.h" - -#include - -#include "../Model.h" -#include "../Symbol/Parse.h" -#include "../XMUtil.h" -#include "Equation.h" -#include "ExpressionList.h" -#include "LeftHandSide.h" -#define YYSTYPE ParseUnion -#include "../Dynamo/DynamoFunction.h" -#include "../Vensim/VYacc.tab.hpp" - -Expression::Expression(SymbolNameSpace *sns) : SymbolTableBase(sns) { -} - -Expression::~Expression(void) { -} - -double Expression::Eval(ContextInfo *info) { - return FLT_MAX; -} - -void Expression::OutputComputable(ContextInfo *info) { -} - -ExpressionFunction::~ExpressionFunction() { - if (HasGoodAlloc()) - delete pArgs; -} - -void ExpressionFunction::CheckPlaceholderVars(Model *m, bool isfirst) { - pArgs->CheckPlaceholderVars(m); -} - -void ExpressionFunction::CheckTableUses(Variable *var) { - if (!pArgs) { - return; - } - // dynamo only this same place in the call logic lets us fill in the table function info - if (pFunction && pFunction->IsTableCall()) { - std::string name = pFunction->GetName(); - if (!static_cast(pFunction)->SetTableXAxis(pArgs)) - log("ERROR TABLE call in equation for %s not correctly formmatted.\n", var->GetName().c_str()); - - if (name == "TABXL") { - // if we get a LOOKUP_EXTRAPOLATE then try to mark the associated lookup - assume all will extrapolate - std::vector vars; - const_cast((*pArgs)[0])->GetVarsUsed(vars); - // the first should be a graphical - std::vector eqs = vars[0]->GetAllEquations(); - for (Equation *eq : eqs) { - Expression *exp = eq->GetExpression(); - if (exp->GetType() == EXPTYPE_Table) - static_cast(exp)->SetExtrapolate(true); - } - } - } - - int n = pArgs->Length(); - for (int i = 0; i < n; i++) { - pArgs->GetExp(i)->CheckTableUses(var); - } -} - -void ExpressionFunction::GetVarsUsed(std::vector &vars) { - if (!pArgs) { - return; - } - - int n = pArgs->Length(); - for (int i = 0; i < n; i++) { - pArgs->GetExp(i)->GetVarsUsed(vars); - } -} - -void ExpressionFunctionMemory::CheckPlaceholderVars(Model *m, bool isfirst) { - if (isfirst || !m) { - pPlacholderEquation = NULL; // deletion is handled by Model - } else { - pPlacholderEquation = m->AddUnnamedVariable(this); - } -} - -static void is_all_plus_minus(Expression *e, FlowList *fl, bool neg) { - if (!e) - fl->SetValid(false); - else if (e->GetType() == EXPTYPE_Variable) { - ExpressionVariable *ev = static_cast(e); - Variable *var = ev->GetVariable(); - if (neg) { - if (var->HasUpstream()) - fl->SetValid(false); - else { - if (var->VariableType() == XMILE_Type_STOCK) - fl->SetValid(false); - fl->AddOutflow(var); - var->SetHasUpstream(true); - } - } else { - if (var->HasDownstream()) - fl->SetValid(false); - else { - if (var->VariableType() == XMILE_Type_STOCK) - fl->SetValid(false); - fl->AddInflow(var); - var->SetHasDownstream(true); - } - } - } else if (e->GetType() == EXPTYPE_Operator) { - const char *op = e->GetOperator(); - if (op) { - if (*op == '\0') // could be () or unary +/- if - we need to flip neg - { - const char *before = e->GetBefore(); - if (before && *before == '-') - neg = !neg; - is_all_plus_minus(e->GetArg(0), fl, neg); - } else if ((*op == '-' || *op == '+') && op[1] == '\0') { - if (e->GetArg(0) != NULL) // unary plus or leading - still okay - is_all_plus_minus(e->GetArg(0), fl, neg); - if (*op == '-') - neg = !neg; - is_all_plus_minus(e->GetArg(1), fl, neg); - } else - fl->SetValid(false); - } else - fl->SetValid(false); - } else - fl->SetValid(false); -} -void ExpressionVariable::GetVarsUsed(std::vector &vars) { - for (Variable *var : vars) { - if (var == pVariable) - return; - } - vars.push_back(pVariable); -} // list of variables used - -bool ExpressionFunctionMemory::TestMarkFlows(SymbolNameSpace *sns, FlowList *fl, Equation *eq) { - if (!this->GetFunction()->IsIntegrator()) { - return false; - } - // only care about active part here - if it all a+b+c-d-e or similar then we are good to go otherwise - // we need to make up a new variable and then use that as the equation in place of what was here - Expression *e = this->GetArgs()->GetExp(0); - if (eq) // make a change - { - assert(fl); - // we set the first argument for the variable in the flow list - ExpressionVariable *ev = new ExpressionVariable(sns, fl->NewVariable(), eq->GetLeft()->GetSubs()); - this->GetArgs()->SetExp(0, ev); - - } else if (fl) // populat flow list - { - is_all_plus_minus(e, fl, false); - fl->SetActiveExpression(e); - } - return true; -} - -void FlowList::AddInflow(Variable *v) { - if (std::find(vInflows.begin(), vInflows.end(), v) != vInflows.end()) - bValid = false; - else if (std::find(vOutflows.begin(), vOutflows.end(), v) != vOutflows.end()) - bValid = false; - else - vInflows.push_back(v); -} - -void FlowList::AddOutflow(Variable *v) { - if (std::find(vInflows.begin(), vInflows.end(), v) != vInflows.end()) - bValid = false; - else if (std::find(vOutflows.begin(), vOutflows.end(), v) != vOutflows.end()) - bValid = false; - else - vOutflows.push_back(v); -} - -bool FlowList::operator==(const FlowList &rhs) { - if (!bValid || !rhs.bValid || vInflows.size() != rhs.vInflows.size() || vOutflows.size() != rhs.vOutflows.size()) - return false; - for (const Variable *v : rhs.vInflows) { - if (std::find(vInflows.begin(), vInflows.end(), v) == vInflows.end()) - return false; - } - for (const Variable *v : rhs.vOutflows) { - if (std::find(vOutflows.begin(), vOutflows.end(), v) == vOutflows.end()) - return false; - } - return true; -} - -void ExpressionLogical::OutputComputable(ContextInfo *info) { - if (pE1) - pE1->OutputComputable(info); - switch (mOper) { - case VPTT_le: - *info << " <= "; - break; - case VPTT_ge: - *info << " >= "; - break; - case VPTT_ne: - *info << " <> "; - break; - case VPTT_and: - *info << " and "; - break; - case VPTT_or: - *info << " or "; - break; - case VPTT_not: - *info << " not "; - break; - - default: - assert(mOper < 128); - *info << ' '; - *info << (char)mOper; - *info << ' '; - break; - } - if (pE2) - pE2->OutputComputable(info); -} - -void ExpressionTable::SetXAxis(Variable *var, double xmin, double xmax, double increment) { - if (increment > 0) { - if (xmax > xmin) { - int count = std::round((xmax - xmin) / increment + 1); - if (count != static_cast(vYVals.size())) - log("Error the table function %s has %d entries but its usage suggests %d\n", var->GetName().c_str(), - (int)vYVals.size(), count); - } else - log("Error the table function %s is used in a table without a proper min/max\n", var->GetName().c_str()); - } else { - log("Error the table function %s is used in a table without an increment\n", var->GetName().c_str()); - increment = 1; - } - for (size_t i = 0; i < vYVals.size(); i++, xmin += increment) - vXVals.push_back(xmin); -} -void ExpressionTable::TransformLegacy() { - assert(!(vXVals.size() % 2)); - size_t n = vXVals.size() / 2; - for (size_t i = 0; i < n; i++) - vYVals[i] = vXVals[n + i]; - vXVals.resize(n); - vYVals.resize(n); -} - -void ExpressionLookup::OutputComputable(ContextInfo *info) { - if (pExpressionVariable) { - *info << "LOOKUP("; - pExpressionVariable->OutputComputable(info); - *info << ", "; - pExpression->OutputComputable(info); - *info << ")"; - } else { - pExpression->OutputComputable(info); - } -} diff --git a/src/xmutil/third_party/xmutil/Symbol/Expression.h b/src/xmutil/third_party/xmutil/Symbol/Expression.h deleted file mode 100644 index 3c41c882c..000000000 --- a/src/xmutil/third_party/xmutil/Symbol/Expression.h +++ /dev/null @@ -1,704 +0,0 @@ -/* Abstract class for expression - the different expression types - are all tiny extensions and included in the same files */ -#ifndef _XMUTIL_SYMBOL_EXPRESSION_H -#define _XMUTIL_SYMBOL_EXPRESSION_H -#include -#include - -#include "../ContextInfo.h" -#include "../XMUtil.h" -#include "../Function/Function.h" -#include "../Function/State.h" -#include "../Function/TableFunction.h" -#include "SymbolList.h" -#include "SymbolTableBase.h" -#include "Variable.h" - -class ExpressionList; // forward declaration -class Model; -class FlowList; -class ExpressionTable; - -// probably don't need these after all -enum EXPTYPE { - EXPTYPE_None, - EXPTYPE_Variable, - EXPTYPE_Symlist, - EXPTYPE_Number, - EXPTYPE_Literal, - EXPTYPE_NumberTable, - EXPTYPE_Function, - EXPTYPE_FunctionMemory, - EXPTYPE_Lookup, - EXPTYPE_Table, - EXPTYPE_Operator -}; - -class Expression : public SymbolTableBase { -public: - Expression(SymbolNameSpace *sns); - virtual ~Expression(void) = 0; - virtual EXPTYPE GetType(void) { - return EXPTYPE_None; - } - virtual double Eval(ContextInfo *info) = 0; - virtual void FlipSign(void) { - } - virtual Function *GetFunction(void) { - return NULL; - } - virtual const char *GetOperator() { - return NULL; - } - virtual const char *GetBefore() { - return NULL; - } - virtual ExpressionTable *GetTable() { - return NULL; - } - virtual void CheckPlaceholderVars(Model *m, bool isfirst) = 0; // generally do nothing, but big error to skip - virtual bool CheckComputed(ContextInfo *info) { - return true; - } - virtual void RemoveFunctionArgs(void) { - } // only 1 subclass does anything - virtual void OutputComputable(ContextInfo *info) = 0; // again don't skip - todo modify this to make dumping - // equations easy - possibly returning std::string - virtual bool IsActiveInit() { - return false; - } - virtual bool TestMarkFlows(SymbolNameSpace *sns, FlowList *fl, - Equation *eq) = 0; // but will also create a flow when the INTEG equation has other stuff - virtual void CheckTableUses(Variable *lhs) { - } - virtual void GetVarsUsed(std::vector &vars) = 0; // list of variables used - virtual void MarkType(XMILE_Type type) = 0; // only called with flow after test returns true - virtual Expression *GetArg(int pos) { - return NULL; - } -}; - -class ExpressionVariable : public Expression { -public: - ExpressionVariable(SymbolNameSpace *sns, Variable *var, SymbolList *subs) : Expression(sns) { - pVariable = var; - pSubList = subs; - } - virtual ~ExpressionVariable(void) { - if (HasGoodAlloc()) { - if (pSubList) - delete pSubList; /* leave pVariable alone */ - } - } - virtual EXPTYPE GetType(void) { - return EXPTYPE_Variable; - } - virtual Variable *GetVariable(void) { - return pVariable; - } - virtual SymbolList *GetSubs() { - return pSubList; - } - void SetSubs(SymbolList *subs) { - pSubList = subs; - } - virtual void CheckPlaceholderVars(Model *m, bool isfirst) { - } - bool CheckComputed(ContextInfo *info) { - return pVariable->CheckComputed(info, false); - } - double Eval(ContextInfo *info) { - return pVariable->Eval(info); - } - virtual void OutputComputable(ContextInfo *info) { - assert(pVariable); - pVariable->OutputComputable(info); - if (pSubList) - pSubList->OutputComputable(info); - } - virtual bool TestMarkFlows(SymbolNameSpace *sns, FlowList *fl, Equation *eq) { - return false; - } // but will also create a flow when the INTEG equation has other stuff - virtual void GetVarsUsed(std::vector &vars); // list of variables used - virtual void MarkType(XMILE_Type type) { - pVariable->SetVariableType(type); - } // only called with flow after test returns true -private: - Variable *pVariable; // pointer back to the model variable - not allocated by this object - SymbolList *pSubList; // subscripts - allocated by this object -}; - -class ExpressionSymbolList : public Expression { -public: - ExpressionSymbolList(SymbolNameSpace *sns, SymbolList *subs, SymbolList *map) : Expression(sns) { - pSymList = subs; - pMap = map; - } - virtual ~ExpressionSymbolList(void) { - if (HasGoodAlloc()) { - if (pSymList) - delete pSymList; - if (pMap) - delete pMap; - } - } - virtual EXPTYPE GetType(void) { - return EXPTYPE_Symlist; - } - virtual void CheckPlaceholderVars(Model *m, bool isfirst) { - } - bool CheckComputed(ContextInfo *info) { - return true; - } - double Eval(ContextInfo *info) { - return -FLT_MAX; - } - virtual void OutputComputable(ContextInfo *info) { - pSymList->OutputComputable(info); - } - virtual bool TestMarkFlows(SymbolNameSpace *sns, FlowList *fl, Equation *eq) { - return false; - } - virtual void GetVarsUsed(std::vector &vars) { - } // list of variables used - virtual void MarkType(XMILE_Type type) { - assert(false); - } - SymbolList *SymList() const { - return pSymList; - } - SymbolList *Map() const { - return pMap; - } - -private: - SymbolList *pSymList; // subscript elements - should be deleted by this object - SymbolList *pMap; // -}; - -class ExpressionNumber : public Expression { -public: - ExpressionNumber(SymbolNameSpace *sns, double num) : Expression(sns) { - value = num; - } - virtual ~ExpressionNumber(void) { - } - virtual EXPTYPE GetType(void) { - return EXPTYPE_Number; - } - void FlipSign(void) { - value = -value; - } - virtual double Eval(ContextInfo *info) { - return value; - } - virtual void CheckPlaceholderVars(Model *m, bool isfirst) { - } - virtual void OutputComputable(ContextInfo *info) { - if (value == -1e38) { - *info << ":na:"; - } else { - *info << StringFromDouble(value); - } - } - virtual bool TestMarkFlows(SymbolNameSpace *sns, FlowList *fl, Equation *eq) { - return false; - } - virtual void GetVarsUsed(std::vector &vars) { - } // list of variables used - virtual void MarkType(XMILE_Type type) { - } - -private: - double value; -}; - -class ExpressionLiteral : public Expression { -public: - ExpressionLiteral(SymbolNameSpace *sns, const char *str) : Expression(sns) { - value = str; - } - virtual ~ExpressionLiteral(void) { - } - virtual EXPTYPE GetType(void) { - return EXPTYPE_Literal; - } - virtual double Eval(ContextInfo *info) { - return -1; - } - virtual void CheckPlaceholderVars(Model *m, bool isfirst) { - } - virtual void OutputComputable(ContextInfo *info) { - *info << value; - } - virtual bool TestMarkFlows(SymbolNameSpace *sns, FlowList *fl, Equation *eq) { - return false; - } - virtual void GetVarsUsed(std::vector &vars) { - } // list of variables used - virtual void MarkType(XMILE_Type type) { - } - -private: - std::string value; -}; -class ExpressionNumberTable : public Expression { -public: - ExpressionNumberTable(SymbolNameSpace *sns) : Expression(sns) { - } - virtual ~ExpressionNumberTable(void) { - } - virtual EXPTYPE GetType(void) { - return EXPTYPE_NumberTable; - } - virtual double Eval(ContextInfo *info) { - return -FLT_MAX; - } - virtual void CheckPlaceholderVars(Model *m, bool isfirst) { - } - virtual void OutputComputable(ContextInfo *info) { - *info << " ??? "; - } - void AddValue(unsigned row, double num) { - vVals.push_back(num); - } // if (row + 1 > vRow.size()) vRow.resize(row + 1); vRow[row].push_back(num); } - // int Count(unsigned row) { if(row < vRow.size()) return 0 ; return vRow[row].size() ; } - const std::vector &GetVals() const { - return vVals; - } - // typedef std::vector ColVals ; - virtual bool TestMarkFlows(SymbolNameSpace *sns, FlowList *fl, Equation *eq) { - return false; - } - virtual void GetVarsUsed(std::vector &vars) { - } // list of variables used - virtual void MarkType(XMILE_Type type) { - } - -private: - std::vector vVals; -}; - -class ExpressionFunction : public Expression { -public: - ExpressionFunction(SymbolNameSpace *sns, Function *f, ExpressionList *args) : Expression(sns) { - pFunction = f; - pArgs = args; - } - ~ExpressionFunction(void); - virtual EXPTYPE GetType(void) override { - return EXPTYPE_Function; - } - virtual double Eval(ContextInfo *info) override { - return pFunction->Eval(this, pArgs, info); - } - virtual Function *GetFunction(void) override { - return pFunction; - } - virtual ExpressionList *GetArgs(void) { - return pArgs; - } - virtual void CheckPlaceholderVars(Model *m, bool isfirst) override; - virtual bool CheckComputed(ContextInfo *info) override { - return pFunction->CheckComputed(info, pArgs); - } - virtual void RemoveFunctionArgs(void) override { - pArgs = NULL; - } - virtual void OutputComputable(ContextInfo *info) override { - pFunction->OutputComputable(info, pArgs); - } - virtual bool IsActiveInit() override { - return pFunction->IsActiveInit(); - } - virtual bool TestMarkFlows(SymbolNameSpace *sns, FlowList *fl, Equation *eq) override { - return false; - } - virtual void CheckTableUses(Variable *lhs) override; - virtual void GetVarsUsed(std::vector &vars) override; // list of variables used - virtual void MarkType(XMILE_Type type) override { - } - -private: - Function *pFunction; // not allocated here - ExpressionList *pArgs; -}; -class ExpressionFunctionMemory : public ExpressionFunction { -public: - ExpressionFunctionMemory(SymbolNameSpace *sns, Function *f, ExpressionList *args) : ExpressionFunction(sns, f, args) { - pPlacholderEquation = NULL; - } - ~ExpressionFunctionMemory(void) { - } - virtual EXPTYPE GetType(void) { - return EXPTYPE_FunctionMemory; - } - virtual double Eval(ContextInfo *info) { - if (pPlacholderEquation) - return pPlacholderEquation->GetVariable()->Eval(info); - return ExpressionFunction::Eval(info); - } - void CheckPlaceholderVars(Model *m, bool isfirst); - bool CheckComputed(ContextInfo *info) { - if (pPlacholderEquation) - return pPlacholderEquation->GetVariable()->CheckComputed(info, false); - return ExpressionFunction::CheckComputed(info); - } - virtual void OutputComputable(ContextInfo *info) { - if (pPlacholderEquation) - return pPlacholderEquation->GetVariable()->OutputComputable(info); - return ExpressionFunction::OutputComputable(info); - } - virtual bool TestMarkFlows(SymbolNameSpace *sns, FlowList *fl, Equation *eq); - virtual void MarkType(XMILE_Type type) { - } - -private: - Equation *pPlacholderEquation; // used in computation (null if function defines LHS) -}; - -class ExpressionLookup : public Expression { -public: - ExpressionLookup(SymbolNameSpace *sns, ExpressionVariable *var, Expression *e) : Expression(sns) { - pExpressionVariable = var; - pExpression = e; - pExpressionTable = NULL; - } - ExpressionLookup(SymbolNameSpace *sns, Expression *e, ExpressionTable *tbl) : Expression(sns) { - pExpressionVariable = NULL; - pExpression = e; - pExpressionTable = tbl; - } - ~ExpressionLookup(void) { - if (HasGoodAlloc()) { - delete pExpressionVariable; - delete pExpression; - } - } - virtual EXPTYPE GetType(void) { - return EXPTYPE_Lookup; - } - virtual ExpressionTable *GetTable(void) { - return pExpressionTable; - } - void CheckPlaceholderVars(Model *m, bool isfirst) { - pExpression->CheckPlaceholderVars(m, false); - } - bool CheckComputed(ContextInfo *info) { - return pExpression->CheckComputed(info); - } - double Eval(ContextInfo *info) { - return TableFunction::Eval(pExpressionVariable, pExpression, info); - } - virtual void OutputComputable(ContextInfo *info); - virtual bool TestMarkFlows(SymbolNameSpace *sns, FlowList *fl, Equation *eq) { - return false; - } - virtual void GetVarsUsed(std::vector &vars) { - if (pExpressionVariable) - pExpressionVariable->GetVarsUsed(vars); - if (pExpression) - pExpression->GetVarsUsed(vars); - } // list of variables used - virtual void MarkType(XMILE_Type type) { - } - -private: - ExpressionVariable *pExpressionVariable; // null for with_lookup - Expression *pExpression; - ExpressionTable *pExpressionTable; -}; - -class ExpressionTable : public Expression { -public: - ExpressionTable(SymbolNameSpace *sns) : Expression(sns) { - bHasRange = false; - bExtrapolate = false; - } - ~ExpressionTable(void) { /* vector destructors only*/ - } - virtual EXPTYPE GetType(void) { - return EXPTYPE_Table; - } - virtual ExpressionTable *GetTable(void) { - return this; - } - void AddPair(double x, double y) { - vXVals.push_back(x); - vYVals.push_back(y); - } - void AddYVal(double y) { - vYVals.push_back(y); - } - void AddRange(double x1, double y1, double x2, double y2) { - bHasRange = true; - dX2 = x2; - dY1 = y1; - dX2 = x2; - dY2 = y2; - } - void SetXAxis(Variable *var, double xmin, double xmax, double xincrerment); - virtual void CheckPlaceholderVars(Model *m, bool isfirst) { - } - double Eval(ContextInfo *info) { - assert(0); - return FLT_MAX; - } - void TransformLegacy(); - std::vector *GetXVals(void) { - return &vXVals; - } - std::vector *GetYVals(void) { - return &vYVals; - } - virtual void OutputComputable(ContextInfo *info) { - *info << "0+0"; - } // unattached table give it a 0+0 equation - points dealt with separately - virtual bool TestMarkFlows(SymbolNameSpace *sns, FlowList *fl, Equation *eq) { - return false; - } - virtual void GetVarsUsed(std::vector &vars) { - } // list of variables used - virtual void MarkType(XMILE_Type type) { - } - bool Extrapolate() { - return bExtrapolate; - } - void SetExtrapolate(bool set) { - bExtrapolate = set; - } - -private: - std::vector vXVals; - std::vector vYVals; - double dY1, dX2, dY2; - bool bHasRange; - bool bExtrapolate; -}; - -class ExpressionOperator2 : public Expression { -public: - ExpressionOperator2(SymbolNameSpace *sns, Expression *e1, Expression *e2) : Expression(sns) { - pE1 = e1; - pE2 = e2; - } - ~ExpressionOperator2(void) { - if (HasGoodAlloc()) { - if (pE1) - delete pE1; - if (pE2) - delete pE2; - } - } - virtual EXPTYPE GetType(void) { - return EXPTYPE_Operator; - } - void CheckPlaceholderVars(Model *m, bool isfirst) { - if (pE1) - pE1->CheckPlaceholderVars(m, false); - if (pE2) - pE2->CheckPlaceholderVars(m, false); - } - bool CheckComputed(ContextInfo *info) { - if (pE1 && !pE1->CheckComputed(info)) - return false; - if (pE2 && !pE2->CheckComputed(info)) - return false; - return true; - } - virtual void OutputComputable(ContextInfo *info) { - } - virtual bool TestMarkFlows(SymbolNameSpace *sns, FlowList *fl, Equation *eq) { - if (pE1 && pE1->TestMarkFlows(sns, fl, eq)) { - return true; - } - if (pE2) { - return pE2->TestMarkFlows(sns, fl, eq); - } - return false; - } - virtual void CheckTableUses(Variable *lhs) { - if (pE1) - pE1->CheckTableUses(lhs); - if (pE2) - pE2->CheckTableUses(lhs); - } - virtual void GetVarsUsed(std::vector &vars) { - if (pE1) - pE1->GetVarsUsed(vars); - if (pE2) - pE2->GetVarsUsed(vars); - } // list of variables used - virtual void MarkType(XMILE_Type type) { - if (pE1) - pE1->MarkType(type); - if (pE2) - pE2->MarkType(type); - } - virtual Expression *GetArg(int pos) { - return pos == 0 ? pE1 : pos == 1 ? pE2 : NULL; - } - -protected: - Expression *pE1; - Expression *pE2; -}; - -#define EO2SubClassRaw(name, evaleq, before, middle, after) \ - class name : public ExpressionOperator2 { \ - public: \ - name(SymbolNameSpace *sns, Expression *e1, Expression *e2) : ExpressionOperator2(sns, e1, e2) { \ - } \ - ~name(void) { \ - } \ - virtual double Eval(ContextInfo *info) { \ - return (evaleq); \ - } \ - virtual const char *GetOperator() { \ - return middle; \ - } \ - virtual const char *GetBefore() { \ - return before; \ - } \ - virtual void OutputComputable(ContextInfo *info) { \ - *info << before; \ - if (pE1) \ - pE1->OutputComputable(info); \ - *info << middle; \ - if (pE2) \ - pE2->OutputComputable(info); \ - *info << after; \ - } \ - }; -#define EO2SubClass(name, evaleq, compsym) EO2SubClassRaw(name, evaleq, "", compsym, ""); - -EO2SubClass(ExpressionMultiply, pE1->Eval(info) * pE2->Eval(info), "*") - EO2SubClass(ExpressionDivide, pE1->Eval(info) / pE2->Eval(info), "/") - EO2SubClass(ExpressionAdd, pE1->Eval(info) + pE2->Eval(info), "+") - EO2SubClass(ExpressionSubtract, pE1->Eval(info) - pE2->Eval(info), "-") - EO2SubClass(ExpressionPower, exp(log(pE1->Eval(info)) * pE2->Eval(info)), "^") - EO2SubClassRaw(ExpressionParen, pE1->Eval(info), "(", "", ")") - EO2SubClassRaw(ExpressionUnaryMinus, (-pE1->Eval(info)), "-", "", "") - - class ExpressionLogical : public Expression { -public: - ExpressionLogical(SymbolNameSpace *sns, Expression *exp1, Expression *exp2, int oper) : Expression(sns) { - pE1 = exp1; - pE2 = exp2; - mOper = oper; - } - ~ExpressionLogical(void) { - if (HasGoodAlloc()) { - delete pE1; - delete pE2; - } - } - virtual double Eval(ContextInfo *info) { - return 0; - } - void CheckPlaceholderVars(Model *m, bool isfirst) { - if (pE1) - pE1->CheckPlaceholderVars(m, false); - if (pE2) - pE2->CheckPlaceholderVars(m, false); - } - void OutputComputable(ContextInfo *info); - virtual bool TestMarkFlows(SymbolNameSpace *sns, FlowList *fl, Equation *eq) { - if (pE1 && pE1->TestMarkFlows(sns, fl, eq)) { - return true; - } - if (pE2) { - return pE2->TestMarkFlows(sns, fl, eq); - } - return false; - } - virtual void CheckTableUses(Variable *lhs) { - if (pE1) - pE1->CheckTableUses(lhs); - if (pE2) - pE2->CheckTableUses(lhs); - } - virtual void GetVarsUsed(std::vector &vars) { - if (pE1) - pE1->GetVarsUsed(vars); - if (pE2) - pE2->GetVarsUsed(vars); - } // list of variables used - virtual void MarkType(XMILE_Type type) { - if (pE1) - pE1->MarkType(type); - if (pE2) - pE2->MarkType(type); - } - -private: - Expression *pE1; - Expression *pE2; - int mOper; -}; - -/* helper class for grabbing flows from an INTEG eqution */ -class FlowList { -public: - FlowList() { - bValid = true; - pActiveExpression = NULL; - pNewVariable = NULL; - } - bool Valid() { - return bValid; - } - void SetValid(bool set) { - bValid = set; - } - bool Empty() { - return vInflows.empty() && vOutflows.empty(); - } - std::vector &Inflows() { - return vInflows; - } - std::vector &Outflows() { - return vOutflows; - } - void AddInflow(Variable *in); - void AddOutflow(Variable *out); - void SetActiveExpression(Expression *expression) { - pActiveExpression = expression; - } - Expression *ActiveExpression() { - return pActiveExpression; - } - void SetNewVariable(Variable *var) { - pNewVariable = var; - } - Variable *NewVariable() { - return pNewVariable; - } - bool operator==(const FlowList &rhs); - -private: - std::vector vInflows; - std::vector vOutflows; - Expression *pActiveExpression; - Variable *pNewVariable; - bool bValid; -}; - -/* -{ - -class ExpressionMultiply : - public Expression -{ -public : - ExpressionMultiply(SymbolNameSpace *sns,Expression *exp1,Expression *exp2) : Expression(sal) {pE1 = exp1;pE2=exp2;} - ~ExpressionMultiply(void) {if(HasGoodAlloc()){delete pE1;delete pE2;}} - virtual double Eval(void) { return pE1->Eval() * pE2->Eval() ; } -private : - Expression *pE1 ; - Expression *pE2 ; -} ; - -*/ - -#endif diff --git a/src/xmutil/third_party/xmutil/Symbol/ExpressionList.cpp b/src/xmutil/third_party/xmutil/Symbol/ExpressionList.cpp deleted file mode 100644 index bc18f503c..000000000 --- a/src/xmutil/third_party/xmutil/Symbol/ExpressionList.cpp +++ /dev/null @@ -1,45 +0,0 @@ -#include "ExpressionList.h" - -#include "../XMUtil.h" - -ExpressionList::ExpressionList(SymbolNameSpace *sns) : SymbolTableBase(sns) { -} - -ExpressionList::~ExpressionList(void) { - if (this->HasGoodAlloc()) { - for (Expression *e : vExpressions) { - delete e; - } - } -} - -void ExpressionList::CheckPlaceholderVars(Model *m) { - for (Expression *e : vExpressions) { - e->CheckPlaceholderVars(m, false); - } -} - -bool ExpressionList::CheckComputed(ContextInfo *info, unsigned wantargs) { - int i = 1; - for (Expression *e : vExpressions) { - if (i & wantargs) { - if (!e->CheckComputed(info)) - return false; - } - i = i << 1; - } - return true; -} - -void ExpressionList::OutputComputable(ContextInfo *info, unsigned wantargs) { - int i = 1; - int j = 0; - for (Expression *e : vExpressions) { - if (i & wantargs) { - if (j++) - *info << ", "; - e->OutputComputable(info); - } - i = i << 1; - } -} diff --git a/src/xmutil/third_party/xmutil/Symbol/ExpressionList.h b/src/xmutil/third_party/xmutil/Symbol/ExpressionList.h deleted file mode 100644 index 19cd8c0ca..000000000 --- a/src/xmutil/third_party/xmutil/Symbol/ExpressionList.h +++ /dev/null @@ -1,37 +0,0 @@ -#ifndef _XMUTIL_EXPRESSIONIST_H -#define _XMUTIL_EXPRESSIONIST_H - -#include - -#include "Expression.h" -class Model; - -class ExpressionList : public SymbolTableBase { -public: - ExpressionList(SymbolNameSpace *sns); - ~ExpressionList(void); - ExpressionList *Append(Expression *last) { - vExpressions.push_back(last); - return this; - } - int Length(void) { - return vExpressions.size(); - } - const Expression *operator[](int pos) const { - return vExpressions[pos]; - } - inline Expression *GetExp(int pos) { - return vExpressions[pos]; - } - void SetExp(int pos, Expression *exp) { - vExpressions[pos] = exp; - } - void CheckPlaceholderVars(Model *m); - bool CheckComputed(ContextInfo *info, unsigned wantargs); - void OutputComputable(ContextInfo *info, unsigned wantargs); - -private: - std::vector vExpressions; -}; - -#endif \ No newline at end of file diff --git a/src/xmutil/third_party/xmutil/Symbol/LeftHandSide.cpp b/src/xmutil/third_party/xmutil/Symbol/LeftHandSide.cpp deleted file mode 100644 index 540ea6bea..000000000 --- a/src/xmutil/third_party/xmutil/Symbol/LeftHandSide.cpp +++ /dev/null @@ -1,34 +0,0 @@ -#include "LeftHandSide.h" - -#include "../XMUtil.h" - -LeftHandSide::LeftHandSide(SymbolNameSpace *sns, ExpressionVariable *var, SymbolList *subs, SymbolListList *exceptlist, - int interpmode) - : SymbolTableBase(sns) { - if (subs) // replace in var - but make copy of var first - more memory leaks - { - pExpressionVariable = new ExpressionVariable(*var); - pExpressionVariable->SetSubs(subs); - } else - pExpressionVariable = var; - pExceptList = exceptlist; - iInterpMode = interpmode; -} - -LeftHandSide::LeftHandSide(SymbolNameSpace *sns, const LeftHandSide &base, Variable *var) : SymbolTableBase(sns) { - pExpressionVariable = new ExpressionVariable(sns, var, base.pExpressionVariable->GetSubs()); - if (base.pExceptList) - pExceptList = new SymbolListList(sns, base.pExceptList); - else - pExceptList = NULL; - iInterpMode = base.iInterpMode; -} - -LeftHandSide::~LeftHandSide(void) { - if (HasGoodAlloc()) { - if (pExpressionVariable) - delete pExpressionVariable; - if (pExceptList) - delete pExceptList; - } -} diff --git a/src/xmutil/third_party/xmutil/Symbol/LeftHandSide.h b/src/xmutil/third_party/xmutil/Symbol/LeftHandSide.h deleted file mode 100644 index affa60958..000000000 --- a/src/xmutil/third_party/xmutil/Symbol/LeftHandSide.h +++ /dev/null @@ -1,35 +0,0 @@ -#ifndef _XMUTIL_SYMBOL_LEFTHANDSIDE_H -#define _XMUTIL_SYMBOL_LEFTHANDSIDE_H - -/* left hand side - mostly just ExpressionVariable but could include some extra - stuff - */ - -#include "Expression.h" -#include "SymbolListList.h" -#include "SymbolTableBase.h" - -class LeftHandSide : public SymbolTableBase { -public: - LeftHandSide(SymbolNameSpace *sns, ExpressionVariable *var, SymbolList *subs, SymbolListList *exceptlist, - int interpmode); - LeftHandSide(SymbolNameSpace *sns, const LeftHandSide &base, Variable *newvar); // copy but put new variable in place - ~LeftHandSide(void); - ExpressionVariable *GetExpressionVariable() { - return pExpressionVariable; - } - Variable *GetVariable(void) { - return pExpressionVariable->GetVariable(); - } - SymbolList *GetSubs() { - return pExpressionVariable->GetSubs(); - } - -private: - LeftHandSide(const LeftHandSide &base); - ExpressionVariable *pExpressionVariable; - SymbolListList *pExceptList; - int iInterpMode; // nonstandard - Vensim for data equations what else? -}; - -#endif \ No newline at end of file diff --git a/src/xmutil/third_party/xmutil/Symbol/NotUsed_SymAllocList.cpp b/src/xmutil/third_party/xmutil/Symbol/NotUsed_SymAllocList.cpp deleted file mode 100644 index befc07eef..000000000 --- a/src/xmutil/third_party/xmutil/Symbol/NotUsed_SymAllocList.cpp +++ /dev/null @@ -1,30 +0,0 @@ -#include "NotUsed_SymAllocList.h" - -#include - -#include "../XMUtil.h" -#include "Symbol.h" - -SymAllocList::SymAllocList(SymNameSpace *ns) { - pSymNameSpace = ns; -} - -SymAllocList::~SymAllocList(void) { - assert(sSymbols.size() == 0); -} - -void SymAllocList::Clean(void) { - std::set::iterator i; - for (i = sSymbols.begin(); i != sSymbols.end(); i++) { - delete (*i); - } - sSymbols.clear(); -} - -void SymAllocList::Reset(void) { - std::set::iterator i; - for (i = sSymbols.begin(); i != sSymbols.end(); i++) { - (*i)->MarkGoodAlloc(); - } - sSymbols.clear(); -} \ No newline at end of file diff --git a/src/xmutil/third_party/xmutil/Symbol/NotUsed_SymAllocList.h b/src/xmutil/third_party/xmutil/Symbol/NotUsed_SymAllocList.h deleted file mode 100644 index cf1fb0428..000000000 --- a/src/xmutil/third_party/xmutil/Symbol/NotUsed_SymAllocList.h +++ /dev/null @@ -1,30 +0,0 @@ -#ifndef _XMUTIL_SYMBOL_SYMALLOCLIST_H -#define _XMUTIL_SYMBOL_SYMALLOCLIST_H - -#include -class SymNameSpace; // forward declaration - -class SymbolTableBase; // forward declaration - -class SymAllocList { -public: - SymAllocList(SymNameSpace *ns); - ~SymAllocList(void); - void Clean(void); - void Reset(void); - void Remove(SymbolTableBase *s) { - sSymbols.erase(s); - } - inline void Add(SymbolTableBase *s) { - sSymbols.insert(s); - } - SymNameSpace *GetNameSpace(void) { - return pSymNameSpace; - } - -private: - std::set sSymbols; - SymNameSpace *pSymNameSpace; -}; - -#endif \ No newline at end of file diff --git a/src/xmutil/third_party/xmutil/Symbol/Parse.h b/src/xmutil/third_party/xmutil/Symbol/Parse.h deleted file mode 100644 index cdd431fe0..000000000 --- a/src/xmutil/third_party/xmutil/Symbol/Parse.h +++ /dev/null @@ -1,50 +0,0 @@ -// Parse.h - just utility definitions for parsing - there is no -// class associated with this but it is used in the parsing -// for the different languages all of which eventually resolve -// to a common set of classes and structures -#ifndef _XMUTIL_PARSE_H -#define _XMUTIL_PARSE_H - -/* define all the types - either with forward class declarations or - as arbitray structure pointers so that all look different to the - compiler */ - -class SymbolList; -class SymbolListList; -class Expression; -class ExpressionList; -class ExpressionTable; -class Equation; -class UnitExpression; -class LeftHandSide; -class ExpressionVariable; -class ExpressionSymbolList; -class Variable; -class Function; - -typedef union _tag_parse_union { - // note everything is 3 letters to make the .y files look a little nicer - int tok; - const char *lit; - SymbolList *sml; - SymbolListList *sll; - Expression *exn; - ExpressionList *exl; - ExpressionVariable *var; - ExpressionSymbolList *esl; - Variable *sym; - Equation *eqn; - UnitExpression *uni; - LeftHandSide *lhs; - Function *fnc; - ExpressionTable *tbl; - double num; - double tol; - -} ParseUnion; - -#ifndef NULL -#define NULL 0 -#endif - -#endif \ No newline at end of file diff --git a/src/xmutil/third_party/xmutil/Symbol/Symbol.cpp b/src/xmutil/third_party/xmutil/Symbol/Symbol.cpp deleted file mode 100644 index ec2fab90e..000000000 --- a/src/xmutil/third_party/xmutil/Symbol/Symbol.cpp +++ /dev/null @@ -1,74 +0,0 @@ -#include "Symbol.h" - -#include "../XMUtil.h" -#include "SymbolNameSpace.h" -#include "Variable.h" - -// Abstract Symbol. Parent to Variable, Function, Token... -// Has a changeable name and returns a type -// does not manage self needs to be part of a collection - -Symbol::Symbol(SymbolNameSpace *sns, const std::string &name) : SymbolTableBase(sns) { - sName = name; - pOwner = NULL; - pSubranges = NULL; - // insert into the name space sns if it has a name - empty get special treatment - if (!name.empty()) - sns->Insert(this); -} - -Symbol::~Symbol(void) { - if (!HasGoodAlloc() && !sName.empty()) { // remove from the lookup table - GetSymbolNameSpace()->Remove(this); - } -} - -const std::string &Symbol::GetName(void) { - return sName; -} - -void Symbol::SetOwner(Symbol *var) { - // static FILE *f; if (!f) f = fopen("c:\\temp\\temp.txt", "w"); - // if(f) fprintf(f, "%s <-- %s", var->GetName().c_str(), this->GetName().c_str()); - if (!pOwner || static_cast(pOwner)->Nelm() < static_cast(var)->Nelm()) { - // if(f)fprintf(f," yes\n"); - var->AddSubrange(this, pOwner); - if (pOwner) - pOwner->SetOwner(var); - pOwner = var; - } - // else if (pOwner == var) - //{ - // if (f)fprintf(f, " already\n"); - // } - // else - //{ - // if(f)fprintf(f," skipped\n"); - // } -} - -void Symbol::AddSubrange(Symbol *sub, Symbol *oldowner) { - if (oldowner) { - std::set *osr = oldowner->Subranges(); - if (osr) { - if (pSubranges) - pSubranges->insert(osr->begin(), osr->end()); - else - pSubranges = osr; - } - } - std::set *osr = sub->Subranges(); - if (osr) { - if (pSubranges) - pSubranges->insert(osr->begin(), osr->end()); - else - pSubranges = osr; - } - if (pSubranges == NULL) - pSubranges = new std::set(); - pSubranges->insert(sub); -} - -SYMTYPE Symbol::isType(void) { - return Symtype_None; -} diff --git a/src/xmutil/third_party/xmutil/Symbol/Symbol.h b/src/xmutil/third_party/xmutil/Symbol/Symbol.h deleted file mode 100644 index 1b334ebe8..000000000 --- a/src/xmutil/third_party/xmutil/Symbol/Symbol.h +++ /dev/null @@ -1,74 +0,0 @@ -#ifndef _XMUTIL_SYMBOL_H -#define _XMUTIL_SYMBOL_H - -#include - -#include "../ContextInfo.h" -#include "../Function/State.h" -#include "SymbolTableBase.h" - -class Model; // forward declaration -class Function; -class Variable; -namespace tinyxml2 { -class XMLElement; -} - -class ModelGroup { -public: - ModelGroup(const std::string &name, ModelGroup *owner) - : sName(name), pOwner(owner), pModule(NULL), pModel(NULL), pVariables(NULL), iDepth(0) { - } - ModelGroup(const std::string &name, ModelGroup *owner, int depth) - : sName(name), pOwner(owner), pModule(NULL), pModel(NULL), pVariables(NULL), iDepth(depth) { - } - std::vector vVariables; - std::string sName; - ModelGroup *pOwner; - tinyxml2::XMLElement *pModule; // this groups module element in the owning module - tinyxml2::XMLElement *pModel; // this groups model elemen in the main document - tinyxml2::XMLElement *pVariables; // variables list in the module - int iDepth; // 0 means root level 1 nested once and so on -}; - -/* abstract class Symbol - used for model vairblaes, models, units - and other things that may appear in the symbol table - these things - share the same search space for lookup but are conceptually distinct */ - -enum SYMTYPE { Symtype_None, Symtype_Variable, Symtype_Units, Symtype_Model, Symtype_Function }; - -class Symbol : public SymbolTableBase { -public: - Symbol(SymbolNameSpace *sns, const std::string &name); - virtual ~Symbol(void) = 0; - virtual SYMTYPE isType(void); - virtual bool CheckComputed(ContextInfo *info, bool first) { - return true; - } // do nothing - virtual void CheckPlaceholderVars(Model *m) { - } // do nothing - virtual void SetupState(ContextInfo *info) { - } // do nothing - virtual int SubscriptCount(std::vector &elmlist) { - return 0; - } - const std::string &GetName(void); - inline void SetName(const std::string &name) { - sName = name; - } - void SetOwner(Symbol *var); - void AddSubrange(Symbol *sub, Symbol *oldowner); - std::set *Subranges() { - return pSubranges; - } - Symbol *Owner() { - return pOwner ? pOwner : this; - } - -private: - std::string sName; - Symbol *pOwner; - std::set *pSubranges; // backward from SetOwber -}; - -#endif // once \ No newline at end of file diff --git a/src/xmutil/third_party/xmutil/Symbol/SymbolList.cpp b/src/xmutil/third_party/xmutil/Symbol/SymbolList.cpp deleted file mode 100644 index 73a6acbda..000000000 --- a/src/xmutil/third_party/xmutil/Symbol/SymbolList.cpp +++ /dev/null @@ -1,113 +0,0 @@ -#include "SymbolList.h" - -#include "../XMUtil.h" -#include "Equation.h" -#include "Variable.h" - -void SymbolList::SymbolListEntry::SetOwner(Variable *var) { - if (eType == EntryType_LIST) - this->u.pSymbolList->SetOwner(var); - else - this->u.pSymbol->SetOwner(var); -} - -SymbolList::SymbolList(SymbolNameSpace *sns, Symbol *first, bool bang) : SymbolTableBase(sns) { - vSymbols.push_back(SymbolListEntry(first, bang)); - pMapRange = NULL; -} - -SymbolList::SymbolList(SymbolNameSpace *sns, SymbolList *first) : SymbolTableBase(sns) { - vSymbols.push_back(SymbolListEntry(first)); - pMapRange = NULL; -} - -SymbolList::~SymbolList(void) { - // do nothing symbols in one hash table or another -} - -// set the owner of array - only if we are bigger than other owners -void SymbolList::SetOwner(Variable *var) { - if (vSymbols.empty()) - return; - std::vector expanded; - for (size_t i = 0; i < vSymbols.size(); i++) { - if (vSymbols[i].eType == EntryType_SYMBOL) { - Equation::GetSubscriptElements(expanded, vSymbols[i].u.pSymbol); - } - } - var->SetNelm(expanded.size()); - // the two that follow might end up doing the same thing depending on the content of the defining list - for (size_t i = 0; i < vSymbols.size(); i++) { - if (vSymbols[i].eType == EntryType_SYMBOL) { - vSymbols[i].u.pSymbol->SetOwner(var); - } - } - for (Symbol *s : expanded) { - s->SetOwner(var); - } - if (expanded[0]->Owner() != var) - var->SetOwner(expanded[0]->Owner()); -} - -void SymbolList::OutputComputable(ContextInfo *info) { - if (vSymbols.empty()) - return; - info->SetInSubList(true); - *info << "["; - for (size_t i = 0; i < vSymbols.size(); i++) { - if (i) - *info << ", "; - if (vSymbols[i].eType == EntryType_SYMBOL) { - // try to find the symbol in the lhs generic list - if there substitue specific otherwise - // use the original symbol - Symbol *s = info->GetLHSSpecific(vSymbols[i].u.pSymbol); - *info << SpaceToUnderBar(s->GetName()); - if (i == vSymbols.size() - 1 && info->WantFinalStar()) - *info << ".*"; - } else if (vSymbols[i].eType == EntryType_BANG_SYMBOL) { - Symbol *s = vSymbols[i].u.pSymbol; - if (s->Owner() != s) { - *info << SpaceToUnderBar(s->GetName()) << ".*"; // new convention for XMILE to allow subrange use - - //// if this is a contiguous subrange we can use a:b notation - otherwise can't do it - // std::vector elms; - // Equation::GetSubscriptElements(elms, s); - // if(elms.size() == 1) - // *info << SpaceToUnderBar(elms[0]->GetName()); - // else - //{ - // std::vector pelms; - // Symbol* owner = s->Owner(); - // while (owner != owner->Owner()) - // owner = owner->Owner(); // this is a bug elsewhere that does not properly reassign owners - // Equation::GetSubscriptElements(pelms, owner); - // int n = elms.size(); - // int m = pelms.size(); - // int i = 0; - // int j = 0; - // for (; j < m; j++) - // { - // if (pelms[j] == elms[i]) - // break; - // } - // for (i = 1; i < n; i++) - // { - // j++; // this is next - // if (j >= m || pelms[j] != elms[i]) - // break; - // } - // if (i == n) - // { - // *info << SpaceToUnderBar(elms.front()->GetName()) << ":" << - // SpaceToUnderBar(elms.back()->GetName()); - // } - // else - // *info << "*" << SpaceToUnderBar(s->GetName()); - // } - } else - *info << "*"; // normally this is all - } - } - *info << "]"; - info->SetInSubList(false); -} diff --git a/src/xmutil/third_party/xmutil/Symbol/SymbolList.h b/src/xmutil/third_party/xmutil/Symbol/SymbolList.h deleted file mode 100644 index 52b9e026c..000000000 --- a/src/xmutil/third_party/xmutil/Symbol/SymbolList.h +++ /dev/null @@ -1,64 +0,0 @@ -#ifndef _XMUTIL_SYMLIST_H -#define _XMUTIL_SYMLIST_H - -#include - -#include "Symbol.h" -#include "SymbolTableBase.h" - -class SymbolList : public SymbolTableBase { -public: - enum EntryType { EntryType_SYMBOL, EntryType_BANG_SYMBOL, EntryType_LIST }; - class SymbolListEntry { - public: - SymbolListEntry(Symbol *s, bool bang) { - u.pSymbol = s; - eType = bang ? EntryType_BANG_SYMBOL : EntryType_SYMBOL; - } - SymbolListEntry(SymbolList *s) { - u.pSymbolList = s; - eType = EntryType_LIST; - } - union { - Symbol *pSymbol; - SymbolList *pSymbolList; - } u; - void SetOwner(Variable *var); - EntryType eType; - }; - SymbolList(SymbolNameSpace *sns, Symbol *first, bool bang); - SymbolList(SymbolNameSpace *sns, SymbolList *first); - ~SymbolList(void); - SymbolList *Append(Symbol *last, bool bang) { - vSymbols.push_back(SymbolListEntry(last, bang)); - return this; - } - SymbolList *Append(SymbolList *next) { - vSymbols.push_back(SymbolListEntry(next)); - return this; - } - int Length(void) { - return vSymbols.size(); - } - const SymbolListEntry &operator[](int pos) const { - return vSymbols[pos]; - } - bool IsMapList() { - return pMapRange != NULL; - } - Symbol *MapRange() { - return pMapRange; - } - void SetMapRange(Symbol *range) { - assert(!pMapRange); - pMapRange = range; - } - void SetOwner(Variable *var); - virtual void OutputComputable(ContextInfo *info); - -private: - std::vector vSymbols; - Symbol *pMapRange; -}; - -#endif diff --git a/src/xmutil/third_party/xmutil/Symbol/SymbolListList.cpp b/src/xmutil/third_party/xmutil/Symbol/SymbolListList.cpp deleted file mode 100644 index 3c3b5aefd..000000000 --- a/src/xmutil/third_party/xmutil/Symbol/SymbolListList.cpp +++ /dev/null @@ -1,22 +0,0 @@ -#include "SymbolListList.h" - -#include "../XMUtil.h" - -SymbolListList::SymbolListList(SymbolNameSpace *sns, SymbolList *first) : SymbolTableBase(sns) { - if (first) - vSymbolLists.push_back(first); - bNoDelete = false; -} - -SymbolListList::SymbolListList(SymbolNameSpace *sns, SymbolListList *orig) : SymbolTableBase(sns) { - vSymbolLists = orig->vSymbolLists; - bNoDelete = true; -} - -SymbolListList::~SymbolListList(void) { - if (bNoDelete) - return; - int n = vSymbolLists.size(); - for (int i = 0; i < n; i++) - delete vSymbolLists[i]; -} diff --git a/src/xmutil/third_party/xmutil/Symbol/SymbolListList.h b/src/xmutil/third_party/xmutil/Symbol/SymbolListList.h deleted file mode 100644 index 661b90a46..000000000 --- a/src/xmutil/third_party/xmutil/Symbol/SymbolListList.h +++ /dev/null @@ -1,30 +0,0 @@ -#ifndef _XMUTIL_SYMBOLLISTLIST_H -#define _XMUTIL_SYMBOLLISTLIST_H - -#include - -#include "SymbolList.h" -#include "SymbolTableBase.h" - -class SymbolListList : public SymbolTableBase { -public: - SymbolListList(SymbolNameSpace *sns, SymbolList *first); - SymbolListList(SymbolNameSpace *sns, SymbolListList *orig); - SymbolListList *Append(SymbolList *last) { - vSymbolLists.push_back(last); - return this; - } - int Length(void) { - return vSymbolLists.size(); - } - const SymbolList *operator[](int pos) const { - return vSymbolLists[pos]; - } - ~SymbolListList(void); - -private: - std::vector vSymbolLists; - bool bNoDelete; -}; - -#endif \ No newline at end of file diff --git a/src/xmutil/third_party/xmutil/Symbol/SymbolNameSpace.cpp b/src/xmutil/third_party/xmutil/Symbol/SymbolNameSpace.cpp deleted file mode 100644 index dcae10140..000000000 --- a/src/xmutil/third_party/xmutil/Symbol/SymbolNameSpace.cpp +++ /dev/null @@ -1,149 +0,0 @@ -#include "SymbolNameSpace.h" - -#include -#include - -#include -#include - -#include "../Unicode.h" -#include "Symbol.h" - -SymbolNameSpace::SymbolNameSpace(void) { - assert(sUnconfirmedAllocations.size() == 0); -} - -SymbolNameSpace::~SymbolNameSpace(void) { - // Collect all symbols first, then clear the hash table before - // deleting them. Symbol::~Symbol() calls Remove() on unconfirmed - // symbols, which would erase from mHashTable and invalidate the - // iterator if we deleted during iteration. - std::vector to_delete; - to_delete.reserve(mHashTable.size()); - for (auto &pair : mHashTable) { - to_delete.push_back(pair.second); - } - mHashTable.clear(); - for (Symbol *sym : to_delete) { - delete sym; - } -} - -Symbol *SymbolNameSpace::Find(const std::string &sin) { - std::string *s = ToLowerSpace(sin); - HashTable::iterator node = mHashTable.find(*s); - delete s; - if (node != mHashTable.end()) - return node->second; - return NULL; -} - -void SymbolNameSpace::Insert(Symbol *sym) { - std::string *s = ToLowerSpace(sym->GetName()); - HashTable::iterator node = mHashTable.find(*s); - if (node != mHashTable.end()) { - assert(node->second == sym); - delete s; - return; /* already in place */ - } - mHashTable[*s] = sym; - delete s; -} - -bool SymbolNameSpace::Remove(Symbol *sym) { - std::string *s = ToLowerSpace(sym->GetName()); - HashTable::iterator node = mHashTable.find(*s); - delete s; - if (node != mHashTable.end()) { - mHashTable.erase(node); - return true; /* already in place */ - } - return false; -} - -bool SymbolNameSpace::Rename(Symbol *sym, const std::string &newname) { - std::string *s = ToLowerSpace(sym->GetName()); - HashTable::iterator oldnode = mHashTable.find(*s); - delete s; - std::string *s2 = ToLowerSpace(newname); - HashTable::iterator newnode = mHashTable.find(*s2); - if (oldnode != mHashTable.end() && newnode == mHashTable.end()) { - mHashTable.erase(oldnode); - mHashTable[*s2] = sym; - delete s2; - sym->SetName(newname); - return true; /* already in place */ - } - delete s2; - return false; -} - -// Note GlobalUCaseMap has to have been set for this to wok - -// not sure if that is thread safe -// -// also note that this works on escaped strings - but does not validate -// escaping beyond stripping a leading " matched to a terminal " (that is -// "this is "not a good" string" would become -// this is "not a good" string -// which is invalid -// -std::string *SymbolNameSpace::ToLowerSpace(const std::string &sin) { - int n = sin.length(); - char *ws = new char[2 * n + 2]; - memcpy(ws, sin.c_str(), n); - if (*ws == '\"' && ws[n - 1] == '\"' && n > 1) { - memcpy(ws, sin.c_str() + 1, n - 2); - n -= 2; - } - ws[n] = '\0'; - - int i, j; - for (i = j = 0; i < n; i++) // remove leading blanks - if (ws[i] != ' ' && ws[i] != '_' && ws[i] != '\t' && ws[i] != '\n' && ws[i] != '\r') - break; - // convert underbars to blanks and compact - for (; i < n; i++) { - if (ws[i] == '\\' && i < n - 1 && ws[i + 1] == '_') { - ws[j++] = '\\'; - ws[j++] = '_'; - i++; // hard underbar treat as a nonspace character - } else if (ws[i] == '_' || ws[i] == ' ' || ws[i] == '\t' || ws[i] == '\n' || ws[i] == '\r') { - for (; i < n - 1; i++) { - if (ws[i + 1] != ' ' && ws[i + 1] != '_' && ws[i + 1] != '\t' && ws[i + 1] != '\n' && ws[i + 1] != '\r') - break; - } - ws[j++] = ' '; - } else - ws[j++] = ws[i]; - } - for (; j > 0; j--) { - if (ws[j - 1] != ' ' && ws[j - 1] != '_' && ws[j - 1] != '\t' && ws[j - 1] != '\n' && ws[j - 1] != '\r') - break; - } - ws[j] = '\0'; - char *ws2 = utf8ToLower(ws, j); - if (ws2 == nullptr) { - throw std::runtime_error("Bad unicode string"); - } - std::string *s = new std::string(ws2); - delete[] ws; - delete[] ws2; - return s; -} - -void SymbolNameSpace::DeleteAllUnconfirmedAllocations(void) { - std::set::iterator i; - while ((i = sUnconfirmedAllocations.begin()) != sUnconfirmedAllocations.end()) { - SymbolTableBase *s = *i; - sUnconfirmedAllocations.erase(i); - delete (s); - } - sUnconfirmedAllocations.clear(); -} - -void SymbolNameSpace::ConfirmAllAllocations(void) { - for (std::set::value_type i : sUnconfirmedAllocations) { - i->MarkGoodAlloc(); - } - sUnconfirmedAllocations.clear(); -} diff --git a/src/xmutil/third_party/xmutil/Symbol/SymbolNameSpace.h b/src/xmutil/third_party/xmutil/Symbol/SymbolNameSpace.h deleted file mode 100644 index 598563561..000000000 --- a/src/xmutil/third_party/xmutil/Symbol/SymbolNameSpace.h +++ /dev/null @@ -1,44 +0,0 @@ -#ifndef _XMUTIL_SYMBOL_NAMESPACE_H -#define _XMUTIL_SYMBOL_NAMESPACE_H -#include -#include -#include -class Symbol; -class SymbolTableBase; // forward declaration - -/* Namespace gives hashed lookup for names - - to make it case and _ insensitive we convert the incoming name before - passing it to the lookup functions - altering the hash and equality - function would (likely) be a bit faster */ - -#define SNSitToSymbol(it) (it.second) -class SymbolNameSpace { -public: - SymbolNameSpace(void); - ~SymbolNameSpace(void); - Symbol *Find(const std::string &name); - void Insert(Symbol *sym); - bool Remove(Symbol *sym); - bool Rename(Symbol *sym, const std::string &newname); - void DeleteAllUnconfirmedAllocations(void); - void ConfirmAllAllocations(void); - void RemoveUnconfirmedAllocation(SymbolTableBase *s) { - sUnconfirmedAllocations.erase(s); - } - inline void AddUnconfirmedAllocation(SymbolTableBase *s) { - sUnconfirmedAllocations.insert(s); - } - typedef std::unordered_map HashTable; - typedef HashTable::value_type iterator; // allows iterator type to be used directly with C++11 range-based for loops - inline HashTable *GetHashTable(void) { - return &mHashTable; - } - static std::string *ToLowerSpace(const std::string &name); - -private: - std::set sUnconfirmedAllocations; - HashTable mHashTable; -}; - -#endif diff --git a/src/xmutil/third_party/xmutil/Symbol/SymbolTableBase.cpp b/src/xmutil/third_party/xmutil/Symbol/SymbolTableBase.cpp deleted file mode 100644 index 6278df301..000000000 --- a/src/xmutil/third_party/xmutil/Symbol/SymbolTableBase.cpp +++ /dev/null @@ -1,14 +0,0 @@ -#include "SymbolTableBase.h" - -#include "../XMUtil.h" - -SymbolTableBase::SymbolTableBase(SymbolNameSpace *sns) { - pSymbolNameSpace = sns; - sns->AddUnconfirmedAllocation(this); -} - -SymbolTableBase::~SymbolTableBase(void) { - if (!HasGoodAlloc()) { // remove from this list no longer part of it - pSymbolNameSpace->RemoveUnconfirmedAllocation(this); - } -} diff --git a/src/xmutil/third_party/xmutil/Symbol/SymbolTableBase.h b/src/xmutil/third_party/xmutil/Symbol/SymbolTableBase.h deleted file mode 100644 index 1a85d6892..000000000 --- a/src/xmutil/third_party/xmutil/Symbol/SymbolTableBase.h +++ /dev/null @@ -1,41 +0,0 @@ -#ifndef _XMUTIL_SYMBOLTABLEBASE_H -#define _XMUTIL_SYMBOLTABLEBASE_H - -// an abstract base class for symbol table control - it is intended to -// help with putting equations out into different formats and also -// has a virtual cleanup function which lets it take care of freeing -// memory before it is destroyed - used to cleanup during exception -// handling when the various objects may not yet be properly connected -// -// another idea is to abstract out some of the serialization -// -// -// - -#include "SymbolNameSpace.h" - -// forward class declarations - used by the concrete classes -class Equation; -class LeftHandSide; -class Expression; -class Variable; - -class SymbolTableBase { -public: - SymbolTableBase(SymbolNameSpace *sns); - inline void MarkGoodAlloc(void) { - pSymbolNameSpace = NULL; - } - inline bool HasGoodAlloc(void) { - return pSymbolNameSpace == NULL; - } - inline SymbolNameSpace *GetSymbolNameSpace(void) { - return pSymbolNameSpace; - } - virtual ~SymbolTableBase(void) = 0; - // SymNameSpace *GetNameSpace() { return pSymbolNameSpace ; } -private: - SymbolNameSpace *pSymbolNameSpace; -}; - -#endif diff --git a/src/xmutil/third_party/xmutil/Symbol/UnitExpression.cpp b/src/xmutil/third_party/xmutil/Symbol/UnitExpression.cpp deleted file mode 100644 index 6a5a600e7..000000000 --- a/src/xmutil/third_party/xmutil/Symbol/UnitExpression.cpp +++ /dev/null @@ -1,82 +0,0 @@ -#include "UnitExpression.h" - -#include "../XMUtil.h" -#include "Units.h" - -UnitExpression::UnitExpression(SymbolNameSpace *sns, Units *num) : SymbolTableBase(sns) { - vNumerator.push_back(num); - dMinVal = dMaxVal = dIncrement = -1e30; // later -} - -UnitExpression::~UnitExpression(void) { -} - -UnitExpression *UnitExpression::Multiply(UnitExpression *mult) { - int i, n; - n = mult->vNumerator.size(); - for (i = 0; i < n; i++) - vNumerator.push_back(mult->vNumerator[i]); - n = mult->vDenominator.size(); - for (i = 0; i < n; i++) - vDenominator.push_back(mult->vDenominator[i]); - delete mult; - Simplify(); - return this; -} -UnitExpression *UnitExpression::Divide(UnitExpression *denom) { - int i, n; - n = denom->vDenominator.size(); - for (i = 0; i < n; i++) - vNumerator.push_back(denom->vDenominator[i]); - n = denom->vNumerator.size(); - for (i = 0; i < n; i++) - vDenominator.push_back(denom->vNumerator[i]); - delete denom; - Simplify(); - return this; -} - -void UnitExpression::Simplify() { - // brute force - int i, j, ntop, nbottom; - ntop = vNumerator.size(); - nbottom = vDenominator.size(); - for (i = 0; i < ntop; i++) { - for (j = 0; j < nbottom; j++) { - if (vNumerator[i] == vDenominator[j]) { - // remove top and bottom - vNumerator.erase(vNumerator.begin() + i); - vDenominator.erase(vDenominator.begin() + j); - i--, j--, ntop--, nbottom--; - break; // inner loop start again next numerator element - } - } - } -} - -std::string UnitExpression::GetEquationString() { - std::string rval; - size_t n = vNumerator.size(); - if (n) { - for (size_t i = 0; i < n; i++) { - if (i) - rval.push_back('*'); - rval += vNumerator[i]->GetName().substr(1); - } - } else - rval = "1"; - n = vDenominator.size(); - if (n) { - rval.push_back('/'); - if (n > 1) - rval.push_back('('); - for (size_t i = 0; i < n; i++) { - if (i) - rval.push_back('*'); - rval += vDenominator[i]->GetName().substr(1); - } - if (n > 1) - rval.push_back(')'); - } - return rval; -} \ No newline at end of file diff --git a/src/xmutil/third_party/xmutil/Symbol/UnitExpression.h b/src/xmutil/third_party/xmutil/Symbol/UnitExpression.h deleted file mode 100644 index ca5576933..000000000 --- a/src/xmutil/third_party/xmutil/Symbol/UnitExpression.h +++ /dev/null @@ -1,30 +0,0 @@ -#ifndef _XMUTIL_SYMBOL_UNITEXPRESSION_H -#define _XMUTIL_SYMBOL_UNITEXPRESSION_H -#include - -#include "SymbolTableBase.h" -class Units; // forward declaration - -class UnitExpression : public SymbolTableBase { -public: - UnitExpression(SymbolNameSpace *sns, Units *num); - ~UnitExpression(void); - UnitExpression *Multiply(UnitExpression *mult); - UnitExpression *Divide(UnitExpression *denom); - std::string GetEquationString(); - inline void SetRange(double minval, double maxval, double increment) { - dMinVal = minval; - dMaxVal = maxval; - dIncrement = increment; - } - -private: - std::vector vNumerator; - std::vector vDenominator; - double dMinVal; - double dMaxVal; - double dIncrement; - void Simplify(void); -}; - -#endif diff --git a/src/xmutil/third_party/xmutil/Symbol/Units.cpp b/src/xmutil/third_party/xmutil/Symbol/Units.cpp deleted file mode 100644 index 49f6afa93..000000000 --- a/src/xmutil/third_party/xmutil/Symbol/Units.cpp +++ /dev/null @@ -1,12 +0,0 @@ -#include "Units.h" - -#include "../XMUtil.h" - -Units::Units(SymbolNameSpace *sns, const std::string &name) : Symbol(sns, name) { - pUnitExpression = NULL; -} - -Units::~Units(void) { - if (pUnitExpression) - delete pUnitExpression; -} diff --git a/src/xmutil/third_party/xmutil/Symbol/Units.h b/src/xmutil/third_party/xmutil/Symbol/Units.h deleted file mode 100644 index 42b37f836..000000000 --- a/src/xmutil/third_party/xmutil/Symbol/Units.h +++ /dev/null @@ -1,21 +0,0 @@ -#ifndef _XMUTIL_SYMBOL_UNITS_H -#define _XMUTIL_SYMBOL_UNITS_H -#include - -#include "Symbol.h" -#include "UnitExpression.h" - -class Units : public Symbol { -public: - Units(SymbolNameSpace *sns, const std::string &name); - ~Units(void); - inline SYMTYPE isType(void) { - return Symtype_Units; - } - -private: - UnitExpression *pUnitExpression; // in case a derived unit of measure - // other stuff? -}; - -#endif diff --git a/src/xmutil/third_party/xmutil/Symbol/Variable.cpp b/src/xmutil/third_party/xmutil/Symbol/Variable.cpp deleted file mode 100644 index e929f5f55..000000000 --- a/src/xmutil/third_party/xmutil/Symbol/Variable.cpp +++ /dev/null @@ -1,514 +0,0 @@ -#include "Variable.h" - -#include - -#include -#include - -#include "../Dynamo/DynamoFunction.h" -#include "../Symbol/Expression.h" -#include "../Symbol/LeftHandSide.h" -#include "../XMUtil.h" - -// model Variable - this has subscript (families) units -// and the comment attached to it - inside of expressions -// we use an ExpressionVariable which has a pointer back -// to this - -Variable::Variable(SymbolNameSpace *sns, const std::string &name) : Symbol(sns, name), _view(NULL), _group(NULL) { - pVariableContent = NULL; - mVariableType = XMILE_Type_UNKNOWN; // till typed - iNelm = 0; - _unwanted = false; - _supplementary = false; - _hasUpstream = _hasDownstream = false; - bAsFlow = false; - bUsesMemory = false; -} - -Variable::~Variable(void) { - if (pVariableContent) { - if (HasGoodAlloc()) - pVariableContent->Clear(); - delete pVariableContent; - pVariableContent = NULL; - } -} - -void Variable::SetViewOfCauses() { - if (_view == NULL || pVariableContent == NULL || _unwanted) - return; // nothing useful to do with this - std::vector eqns = pVariableContent->GetAllEquations(); - for (Equation *eqn : eqns) { - std::vector vars; - eqn->GetVarsUsed(vars); - for (Variable *var : vars) { - if (var->_view == NULL) { - var->_view = _view; - var->SetViewOfCauses(); // recur - } - } - } -} - -void Variable::SetViewToCause(int depth) { - if (_view != NULL || pVariableContent == NULL || _unwanted || depth == 0) - return; // nothing useful to do with this - depth--; - std::vector eqns = pVariableContent->GetAllEquations(); - for (Equation *eqn : eqns) { - std::vector vars; - eqn->GetVarsUsed(vars); - for (Variable *var : vars) { - var->SetViewToCause(depth); // recur - if (var->_view != NULL) { - _view = var->_view; - return; - } - } - } -} - -std::string Variable::GetAlternateName(void) { - std::string name = pVariableContent ? pVariableContent->GetAlternateName() : GetName(); - // strip out surrounding quotes if they exist - we want to deliver the name without them - if (name.size() > 2 && name[0] == '\"' && name.back() == '\"' && name.find('.') == std::string::npos) - name = name.substr(1, name.size() - 2); - return name; -} - -void Variable::PurgeAFOEq() { - if (!pVariableContent) - return; - std::vector equations = pVariableContent->GetAllEquations(); - // drop any equations with no expressions - Vensim allos a= as an equation but it is not valid - std::vector drop; - int i = 0; - for (Equation *eq : equations) { - if (eq->GetExpression() == NULL) - drop.push_back(i); - i++; - } - if (!drop.empty()) { - for (int i = drop.size(); i-- > 0;) { - pVariableContent->DropEquation(drop[i]); - } - equations = pVariableContent->GetAllEquations(); - } - if (equations.size() <= 1) - return; - // if the first equation is an A FUNCTION OF equation then delete it - Equation *eq = equations[0]; - Expression *exp = eq->GetExpression(); - if (exp->GetType() == EXPTYPE_Function) { - Function *f = exp->GetFunction(); - if (f->GetName() == "A FUNCTION OF") - pVariableContent->DropEquation(0); - } -} - -XMILE_Type Variable::MarkTypes(SymbolNameSpace *sns) { - if (!pVariableContent) - return mVariableType; - - std::vector equations = pVariableContent->GetAllEquations(); - - if (equations.empty()) { - equations = pVariableContent->GetAllInitEquations(); - // todo data variables have empty equations - could fill something in here??? - if (equations.empty()) - return mVariableType; - } - - /* if the equations are INTEG this is a stock and we need to validate flows - if we need to make - up flows it has to be done here so that all the equations get the same net flow name - we make up flows if the active part of INTEG uses something other then +/- of flows or - if there are multiple equations that don't match (even if they all use +/- of flows) - - */ - // first pass - just figure out if there is anything to do - - bool gotone = false; - size_t n = equations.size(); - for (size_t i = 0; i < n; i++) { - Equation *eq = equations[i]; - Expression *exp = eq->GetExpression(); - if (exp) { - if (exp->GetType() == EXPTYPE_Symlist) { - // the array should be a list of elements - we need to point those back to the array so that we can propery - // dimension variables - SymbolList *symlist = static_cast(exp)->SymList(); - std::string name = this->GetName(); - symlist->SetOwner(this); // this can recur - mVariableType = XMILE_Type_ARRAY; - // anything untyped in the symlist should be marked an elm - may contain other arrays - int n = symlist->Length(); - for (int i = 0; i < n; i++) { - const SymbolList::SymbolListEntry &sle = (*symlist)[i]; - if (sle.eType == SymbolList::EntryType_SYMBOL) { - const Variable *elm = static_cast(sle.u.pSymbol); - if (elm->mVariableType == XMILE_Type_UNKNOWN) - const_cast(elm)->mVariableType = XMILE_Type_ARRAY_ELM; - } - } - return mVariableType; - } else if (exp->GetType() == EXPTYPE_NumberTable) { - // need to blow this out to separate equations for allentries - only implemented for the single equation version - // right now - std::vector> elms; - std::vector subs; - eq->SubscriptExpand(elms, subs); - if (!elms.empty()) // can do something - otherwise just put out ??? - { - ExpressionNumberTable *t = static_cast(exp); - const std::vector &vals = t->GetVals(); - if (vals.size() != elms.size()) { - log("Error the number of entries does not match array size for \"%s\"\n", this->GetName().c_str()); - } else { - equations.erase(equations.begin() + i); - size_t n2 = vals.size(); - for (size_t j = 0; j < n2; j++) { - SymbolList *entry = new SymbolList(sns, elms[j][0], SymbolList::EntryType_SYMBOL); - for (size_t k = 1; k < elms[j].size(); k++) - entry->Append(elms[j][k], false); - LeftHandSide *lhs = new LeftHandSide(sns, eq->GetLeft()->GetExpressionVariable(), entry, NULL, 0); - ExpressionNumber *expnum = new ExpressionNumber(sns, vals[j]); - Equation *neq = new Equation(sns, lhs, expnum, '='); - equations.push_back(neq); - } - // now reenter with new equations - pVariableContent->SetAllEquations(equations); - return MarkTypes(sns); - } - } - } else if (exp->GetType() == EXPTYPE_Function) { - Function *function = static_cast(exp)->GetFunction(); - if (function->IsDelay()) - this->MarkUsesMemory(); - } - if (exp->TestMarkFlows(sns, NULL, NULL)) { - gotone = true; - break; // one is all that is needed - } - exp->CheckTableUses(this); - } - } - if (!gotone) { - if (mVariableType == XMILE_Type_UNKNOWN) { - // check to see if this is decorated as a flow - if (this->AsFlow()) - mVariableType = XMILE_Type_FLOW; - else if (this->UsesMemory()) - mVariableType = XMILE_Type_DELAYAUX; - else - mVariableType = XMILE_Type_AUX; - } - return mVariableType; - } - mVariableType = XMILE_Type_STOCK; - return mVariableType; -} - -void Variable::MarkStockFlows(SymbolNameSpace *sns) { - // second pass, get the flow lists for everyone -- NOTE there is a bug in this code - // because we don't check subscripts on the flows list so they may match even though - // they shouldn't eg STOCK[A]=INTEG(FLOW[B],0) STOCK[B]=INTEG(FLOW[A],0) - - if (mVariableType != XMILE_Type_STOCK) - return; - std::vector equations = pVariableContent->GetAllEquations(); - if (equations.empty()) - return; - - std::vector flow_lists; - flow_lists.resize(equations.size()); - size_t i = 0; - bool match = true; - for (Equation *eq : equations) { - Expression *exp = eq->GetExpression(); - if (!exp->TestMarkFlows(sns, &flow_lists[i], NULL) || !flow_lists[i].Valid()) - match = false; - else if (i > 0 && !(flow_lists[i] == flow_lists[i - 1])) - match = false; // all must be the same - i++; - } - if (match) { - for (Variable *v : flow_lists[0].Inflows()) { - v->SetVariableType(XMILE_Type_FLOW); - mInflows.push_back(v); - } - for (Variable *v : flow_lists[0].Outflows()) { - v->SetVariableType(XMILE_Type_FLOW); - mOutflows.push_back(v); - } - return; // done - } - - // mismatched for invalid flow equations - create a flow variable and add it to the model - std::string basename = this->GetName() + " net flow"; - std::string name = basename; - i = 0; - while (sns->Find(name)) { - ++i; - name = basename + "_" + std::to_string(i); - } - Variable *v = new Variable(sns, name); - v->SetVariableType(XMILE_Type_FLOW); - v->SetView(this->GetView()); - ModelGroup *group = this->GetGroup(); - if (group) { - v->SetGroup(group); - group->vVariables.push_back(v); - } - mInflows.push_back(v); - - // now we swap the active part of the INTEG equation for v and set v's equation to - // the active part - this is equation by equation - i = 0; - for (Equation *eq : equations) { - // left hand side for this variable - LeftHandSide *lhs = new LeftHandSide(sns, *eq->GetLeft(), v); // replace var in lhs equation - Equation *neweq = new Equation(sns, lhs, flow_lists[i].ActiveExpression(), '='); - v->AddEq(neweq); - flow_lists[i].SetNewVariable(v); - eq->GetExpression()->TestMarkFlows(sns, &flow_lists[i], eq); - i++; - } - // don't do this - we get some memory leakage but risk a crash otherwise v->MarkGoodAlloc(); -} - -void VariableContent::Clear(void) { - if (pState) { - delete pState; - pState = 0; - } -} - -void VariableContentVar::Clear(void) { - // clear memory - VariableContent::Clear(); - if (pUnits) - delete pUnits; - int i; - int n = vSubscripts.size(); - for (i = 0; i < n; i++) - delete vSubscripts[i]; - n = vEquations.size(); - for (i = 0; i < n; i++) - delete vEquations[i]; - // comment takes care of itself -} -std::vector VariableContentVar::GetInputVars() { - std::vector vars; - for (Equation *eq : this->vEquations) - eq->GetVarsUsed(vars); - return vars; -} - -std::vector VariableContentVar::GetInitInputVars() { - std::vector vars; - for (Equation *eq : this->vInitEquations) - eq->GetVarsUsed(vars); - return vars; -} - -void Variable::AddEq(Equation *eq, bool init) { - if (!pVariableContent) { - try { - pVariableContent = new VariableContentVar; - pVariableContent->SetAlternateName(this->GetName()); // until overidden - } catch (...) { - throw std::runtime_error("Memory failure adding equations"); - } - } - pVariableContent->AddEq(eq, init); -} - -void Variable::OutputComputable(ContextInfo *info) { - if (info->LHS() == this) { - if (info->SelfIsPrevious()) - *info << "PREVIOUS(SELF, NAN)"; - else - *info << "SELF"; - return; - } - if (this->mVariableType == XMILE_Type_ARRAY) { - Symbol *s = info->GetLHSSpecific(this); - if (s && s != this) { - *info << SpaceToUnderBar(this->GetName()) << "."; - *info << SpaceToUnderBar(s->GetName()); - return; - } - } - if (pVariableContent) - pVariableContent->OutputComputable(info); - else { - if (mVariableType == XMILE_Type_ARRAY_ELM && !info->InSubList()) - *info << SpaceToUnderBar(this->Owner()->GetName()) << "."; - *info << SpaceToUnderBar(GetName()); - } -} - -VariableContent::VariableContent(void) { - pState = NULL; -} - -VariableContent::~VariableContent(void) { -} - -void VariableContentVar::CheckPlaceholderVars(Model *m) { - for (Equation *eq : vEquations) { - eq->CheckPlaceholderVars(m); - } -} - -bool VariableContentVar::CheckComputed(Symbol *parent, ContextInfo *info, bool first) { - // log("Checking out %s\n",GetName().c_str()) ; - if (!pState) { - // log(" - not computable ignoring\n") ; - return true; - } - if (pState->cComputeFlag & info->GetComputeType()) { - if (info->GetComputeType() == CF_active) { - if (pState->HasMemory()) - info->AddDDF(DDF_level); // not recorded in cDynamicDependencyFlag - else - info->AddDDF(pState->cDynamicDependencyFlag); - } - return true; // done - } - int intype = info->GetComputeType() << 1; - if (pState->cComputeFlag & intype) { - if (info->GetComputeType() == CF_initial) - log("Simultaneous initial equations found \n"); - else { - if (pState->HasMemory()) { // first call was for rates - now for level - assert(!first); - info->AddDDF(DDF_level); - return true; - } - log("Simultaneous active equations found \n"); - } - log(" %s\n", parent->GetName().c_str()); - pState->cComputeFlag &= ~intype; - return false; - } else if (!first && (info->GetComputeType() != CF_initial) && pState->HasMemory()) { - // log("Not tracing further for level %s\n",GetName().c_str()) ; - info->AddDDF( - DDF_level); // this is the level - even if the rate is a constant (only a 0 rate would really be unchanging) - return true; - } else if (first && info->GetComputeType() == CF_initial && !pState->HasMemory()) { - return true; /* don't need to do anything, if var is needed will be called not first */ - } else { // really need to check - unsigned char ddf = info->GetDDF(); - if (info->GetComputeType() == CF_active) { - info->ClearDDF(); - if (!pState->UpdateOnPartialStep()) - info->AddDDF(DDF_time_varying); - } - pState->cComputeFlag |= intype; - for (Equation *e : vEquations) { - if (!e->GetExpression()->CheckComputed(info)) { - log(" %s\n", parent->GetName().c_str()); - pState->cComputeFlag &= ~intype; - pState->cComputeFlag |= info->GetComputeType(); // don't reenter - return false; - } - } - if (info->GetComputeType() == CF_active) { - pState->cDynamicDependencyFlag = info->GetDDF(); - info->AddDDF(ddf); - } - } - - pState->cComputeFlag = (pState->cComputeFlag | info->GetComputeType()) & (~intype); - - /* add the equations - some can be ignored */ - if (info->GetComputeType() == CF_active) { - if (pState->HasMemory() || !(pState->cDynamicDependencyFlag & (DDF_level | DDF_data | DDF_time_varying))) - return true; - } else if (info->GetComputeType() == CF_unchanging) { - if (pState->HasMemory() || pState->cDynamicDependencyFlag & (DDF_level | DDF_data | DDF_time_varying)) - return true; - } else if (info->GetComputeType() == CF_rate) { - if (!pState->HasMemory()) - return true; - } - log("Outputting equations for %s\n", parent->GetName().c_str()); - for (Equation *e : vEquations) { - info->PushEquation(e); - } - return true; -} - -void VariableContentVar::SetupState(ContextInfo *info) { - bool hasmemory = false; - bool timedependent = false; - if (!info) { - if (pState) - delete pState; - pState = NULL; - return; - } - if (!pState) { /* create it - actually this is called twice with value assignment on second pass */ - if (info->GetComputeType()) - return; // do nothing no state allocated on the previous try - // find out what defines this to determine type - bool haseq = false; - for (Equation *e : vEquations) { - haseq = true; - if (e->GetExpression()->GetType() == EXPTYPE_Table) { - return; // for now no state assigned - } - Function *f = e->GetExpression()->GetFunction(); - if (f) { - if (!f->IsMemoryless()) - hasmemory = true; - if (f->IsTimeDependent()) - timedependent = true; - } - } - // empty equation causes what??? - if (!haseq) - timedependent = true; // consistent with exog variables - if (hasmemory) - pState = new StateLevel(info->GetSymbolNameSpace()); - else if (timedependent) - pState = new StateTime(info->GetSymbolNameSpace()); - else - pState = new State(info->GetSymbolNameSpace()); - pState->iNVals = 1; // todo arrays - } - // the following addresses will be nonsense on the first pass we just - // use them to set the requires size for the integration state vector - if (pState->HasMemory()) { - pState->pVals = info->GetLevelP(pState->iNVals); - pState->SetRateP(info->GetRateP(pState->iNVals)); - } else - pState->pVals = info->GetAuxP(pState->iNVals); -} - -int VariableContentVar::SubscriptCount(std::vector &elmlist) { - if (vEquations.empty()) - return 0; - int count = vEquations[0]->SubscriptCount(elmlist); - if (count > 0) { - if (vEquations.size() != 1) { - for (size_t i = 1; i < vEquations.size(); i++) { - std::vector other; - if (vEquations[0]->SubscriptCount(other) != count) - throw std::runtime_error("Bad subscript equations"); - } - } - // we need to get to the array not the elements for elmlist - not map to parent only if multiple equations - // for (int i = 0; i < count; i++) - //{ - // Variable* sym = elmlist[i]; - // //Symbol* var = sym->Owner(); - // if (sym) - // elmlist[i] = sym; - //} - - return count; - } - return 0; -} diff --git a/src/xmutil/third_party/xmutil/Symbol/Variable.h b/src/xmutil/third_party/xmutil/Symbol/Variable.h deleted file mode 100644 index 009ad8f3a..000000000 --- a/src/xmutil/third_party/xmutil/Symbol/Variable.h +++ /dev/null @@ -1,376 +0,0 @@ -#ifndef _XMUTIL_SYMBOL_VARIABLE_H -#define _XMUTIL_SYMBOL_VARIABLE_H -#include -#include - -#include "../Function/Function.h" -#include "../Function/State.h" -#include "../XMUtil.h" -#include "Equation.h" -#include "Symbol.h" -#include "UnitExpression.h" -class View; - -/* varibale content - the true stuff for variables without the associated symbol - the indirect subclassing is necessary because the variable type is not necessarily - known when it is entered into the symbol table - contrast this with Function which - is all just a class - - It might also be possible to incorporate State directly into VariableContent, but - right now it is convenient to completely clear states when analyzing a model - - VariableContent is an abstract class - */ -enum XMILE_Type { - XMILE_Type_UNKNOWN, - XMILE_Type_AUX, - XMILE_Type_DELAYAUX, - XMILE_Type_STOCK, - XMILE_Type_FLOW, - XMILE_Type_ARRAY, - XMILE_Type_ARRAY_ELM -}; -class VariableContent { -public: - VariableContent(void); - virtual ~VariableContent(void) = 0; - friend class Variable; - friend class VariableContentVar; - inline double Eval(ContextInfo *info) { - return pState->GetValue(0); - } // todo subscritps - inline void SetInitialValue(int off, double val) { - pState->SetInitialValue(off, val); - } - inline void SetActiveValue(int off, double val) { - pState->SetActiveValue(off, val); - } - - virtual bool CheckComputed(Symbol *parent, ContextInfo *info, bool first) { - return true; - } - virtual void Clear(void); - virtual void AddEq(Equation *eq, bool init) { - assert(false); - } - virtual Equation *GetEquation(int pos) { - return NULL; - } - virtual std::vector GetAllEquations() { - return std::vector(); - } - virtual std::vector GetAllInitEquations() { - return std::vector(); - } - virtual void DropEquation(int pos) { - } - virtual void SetAllEquations(std::vector set) { - assert(false); - } - virtual std::vector GetInputVars() { - return std::vector(); - } - virtual std::vector GetInitInputVars() { - return std::vector(); - } - virtual bool AddUnits(UnitExpression *un) { - return false; - } - virtual UnitExpression *Units() { - return NULL; - } - virtual void OutputComputable(ContextInfo *info) { - assert(0); - } - virtual void CheckPlaceholderVars(Model *m) { - } - virtual void SetupState(ContextInfo *info) { - } // returns number of entries in state vector required (states can also claim thier own storage) - virtual void SetAlternateName(const std::string &altname) { - } - virtual const std::string &GetAlternateName(void) { - assert(0); - std::string *s = new std::string; - return *s; - } - virtual int SubscriptCount(std::vector &elmlist) { - return 0; - } - virtual XMILE_Type MarkFlows(SymbolNameSpace *sns, Variable *parent, XMILE_Type intype) { - return XMILE_Type_UNKNOWN; - } - -protected: - State *pState; // different kinds of states, also compute flags are here along with var type -}; -class VariableContentSub : public VariableContent { -public: - VariableContentSub(Variable *v) { - pFamily = v; - } - ~VariableContentSub(void) { - } - -private: - Variable *pFamily; - std::vector vElements; // includes count -}; -class VariableContentElm : public VariableContent { -public: - VariableContentElm(Variable *v) { - pFamily = v; - } - ~VariableContentElm(void) { - } - -private: - Variable *pFamily; -}; -class VariableContentVar : public VariableContent { -public: - VariableContentVar(void) { - pUnits = NULL; - } - ~VariableContentVar(void) { - } - // friend class Variable ; - - bool CheckComputed(Symbol *parent, ContextInfo *info, bool first); - void Clear(void); - void AddEq(Equation *eq, bool init) { - if (init) - vInitEquations.push_back(eq); - else - vEquations.push_back(eq); - } - virtual Equation *GetEquation(int pos) { - return vEquations[pos]; - } - virtual std::vector GetAllEquations() { - return vEquations; - } - virtual std::vector GetAllInitEquations() { - return vInitEquations; - } - virtual void DropEquation(int pos) { - vEquations.erase(vEquations.begin() + pos); - } - virtual void SetAllEquations(std::vector set) { - vEquations = set; - } - virtual std::vector GetInputVars(); - virtual std::vector GetInitInputVars(); - bool AddUnits(UnitExpression *un) { - if (!pUnits) { - pUnits = un; - return true; - } - return false; - } - UnitExpression *Units() { - return pUnits; - } - void OutputComputable(ContextInfo *info) { - *info << SpaceToUnderBar(sAlternateName); - } - void CheckPlaceholderVars(Model *m); - void SetupState(ContextInfo *info); // returns number of entries in state vector required (states can also claim - // thier own storage) - void SetAlternateName(const std::string &altname) { - sAlternateName = altname; - } - const std::string &GetAlternateName(void) { - return sAlternateName; - } - virtual int SubscriptCount(std::vector &elmlist); - -protected: - std::vector - vSubscripts; // for regular variables the family's for subscripts the family (possibly self) follwed by elements - std::vector vEquations; - std::vector vInitEquations; - std::string Comment; // arbitrary UTF8 string - std::string sAlternateName; // for writing out equations as computer code - UnitExpression *pUnits; // units could be attached to equations -}; - -class Variable : public Symbol { -public: - Variable(SymbolNameSpace *sns, const std::string &name); - ~Variable(void); - // virtual functions - inline SYMTYPE isType(void) { - return Symtype_Variable; - } - View *GetView() { - return _view; - } - void SetView(View *view) { - _view = view; - } - void SetViewOfCauses(); - void SetViewToCause(int depth); - - ModelGroup *GetGroup() const { - return _group; - } - void SetGroup(ModelGroup *set) { - _group = set; - } - - void SetComment(const std::string &com) { - _comment = com; - } - void SetUnitsString(const std::string &set) { - _units = set; - } - const std::string &GetUnitsString() const { - return _units; - } - bool Unwanted() const { - return _unwanted; - } - void SetUnwanted(bool set) { - _unwanted = set; - } - bool Supplementary() const { - return _supplementary; - } - void SetSupplementary(bool set) { - _supplementary = set; - } - const std::string &Comment() { - return _comment; - } - // virtuals passed on to content - need to keep pVariableContent populated before calling - bool CheckComputed(ContextInfo *info, bool first) { - if (pVariableContent) - return pVariableContent->CheckComputed(this, info, first); - return false; - } - void CheckPlaceholderVars(Model *m) { - if (pVariableContent) - pVariableContent->CheckPlaceholderVars(m); - } - void SetupState(ContextInfo *info) { - if (pVariableContent) - pVariableContent->SetupState(info); - } - int SubscriptCountVars(std::vector &elmlist) { - return pVariableContent ? pVariableContent->SubscriptCount(elmlist) : 0; - } - // passthrough calls - many of these are virtual in VariableContent or passed through to yet another class - void AddEq(Equation *eq, bool init = false); - inline Equation *GetEquation(int pos) { - return pVariableContent->GetEquation(pos); - } - std::vector GetAllEquations() { - return pVariableContent ? pVariableContent->GetAllEquations() : std::vector(); - } - std::vector GetAllInitEquations() { - return pVariableContent ? pVariableContent->GetAllInitEquations() : std::vector(); - } - inline bool AddUnits(UnitExpression *un) { - return pVariableContent->AddUnits(un); - } - UnitExpression *Units() { - return pVariableContent ? pVariableContent->Units() : NULL; - } - void OutputComputable(ContextInfo *info); - inline double Eval(ContextInfo *info) { - return pVariableContent->Eval(info); - } - inline std::vector GetInputVars() { - return pVariableContent ? pVariableContent->GetInputVars() : std::vector(); - } - inline std::vector GetInitInputVars() { - return pVariableContent ? pVariableContent->GetInitInputVars() : std::vector(); - } - inline void SetInitialValue(int off, double val) { - pVariableContent->SetInitialValue(off, val); - } - inline void SetActiveValue(int off, double val) { - pVariableContent->SetActiveValue(off, val); - } - inline void SetAlternateName(const std::string &altname) { - pVariableContent->SetAlternateName(altname); - } - std::string GetAlternateName(void); - - void PurgeAFOEq(); - XMILE_Type MarkTypes(SymbolNameSpace *sns); // mark the variableType of inflows/outflows - void MarkStockFlows(SymbolNameSpace *sns); // mark the variableType of inflows/outflows - XMILE_Type VariableType() { - return mVariableType; - } - void SetVariableType(XMILE_Type t) { - mVariableType = t; - } - - void MarkAsFlow() { - bAsFlow = true; - } - bool AsFlow() const { - return bAsFlow; - } - void MarkUsesMemory() { - bUsesMemory = true; - } - bool UsesMemory() const { - return bUsesMemory; - } - - size_t Nelm() const { - return iNelm; - } - void SetNelm(size_t set) { - iNelm = set; - } - - // for flowing - bool HasUpstream() const { - return _hasUpstream; - } - void SetHasUpstream(bool set) { - _hasUpstream = set; - } - bool HasDownstream() const { - return _hasDownstream; - } - void SetHasDownstream(bool set) { - _hasDownstream = set; - } - - // for other function calles - inline VariableContent *Content(void) { - return pVariableContent; - } - void SetContent(VariableContent *v) { - pVariableContent = v; - } - std::vector &Inflows() { - return mInflows; - } - std::vector &Outflows() { - return mOutflows; - } - // virtual -private: - std::string _comment; - std::string _units; - std::vector mInflows; // only ued for stocks - should push to VariableConent - std::vector mOutflows; - VariableContent *pVariableContent; // dependent on variable type which is not known on instantiation - XMILE_Type mVariableType; - size_t iNelm; // used for subscript owners - View *_view; // view defined in - ModelGroup *_group; - bool _unwanted; - bool _supplementary; - bool _hasUpstream; - bool _hasDownstream; - bool bAsFlow; - bool bUsesMemory; -}; - -#endif diff --git a/src/xmutil/third_party/xmutil/Unicode.cpp b/src/xmutil/third_party/xmutil/Unicode.cpp deleted file mode 100644 index 13963b512..000000000 --- a/src/xmutil/third_party/xmutil/Unicode.cpp +++ /dev/null @@ -1,37 +0,0 @@ -// Unicode.cpp : Wraps unicode-specific functionality - -#include "Unicode.h" - -#include - -#include "libutf/utf.h" - -bool OpenUnicode() { - return true; -} - -void CloseUnicode() { -} - -char *utf8ToLower(const char *src, size_t srcLen) { - int n; - Rune u; - - size_t dstLen = 0; - for (size_t srcOff = 0; srcOff < srcLen && *src > 0 && (n = chartorune(&u, &src[srcOff])); srcOff += n) { - const Rune l = tolowerrune(u); - dstLen += runelen(l); - } - - char *dst = new char[dstLen + 1]; - memset(dst, 0, dstLen + 1); - - size_t dstOff = 0; - for (size_t srcOff = 0; srcOff < srcLen && *src > 0 && (n = chartorune(&u, &src[srcOff])); srcOff += n) { - Rune l = tolowerrune(u); - const int size = runetochar(&dst[dstOff], &l); - dstOff += size; - } - - return dst; -} diff --git a/src/xmutil/third_party/xmutil/Unicode.h b/src/xmutil/third_party/xmutil/Unicode.h deleted file mode 100644 index 8620248e9..000000000 --- a/src/xmutil/third_party/xmutil/Unicode.h +++ /dev/null @@ -1,13 +0,0 @@ -#ifndef _XMUTIL_UNICODE_H -#define _XMUTIL_UNICODE_H - -#include - -// OpenUnicode returns true if we successfully initialized global unicode state. -bool OpenUnicode(); -// CloseUnicode cleans up/frees global unicode state, and should only be called on program close. -void CloseUnicode(); - -char *utf8ToLower(const char *src, size_t srcLen); - -#endif \ No newline at end of file diff --git a/src/xmutil/third_party/xmutil/Vensim/VLex.y b/src/xmutil/third_party/xmutil/Vensim/VLex.y deleted file mode 100644 index 882f60057..000000000 --- a/src/xmutil/third_party/xmutil/Vensim/VLex.y +++ /dev/null @@ -1,57 +0,0 @@ -/* scanner for a toy Pascal-like language */ - -%{ -/* need this for the call to atof() below */ -#include -#include /* printf, fgets */ -#include /* atoi */ -#include -#define YY_NO_UNISTD_H -%} -%option never-interactive -%option noyywrap - - -%% - -[0-9]+ { - printf( "An integer: %s (%d)\n", yytext, - atoi( yytext ) ); - } - -["][^"]*["] { - printf("A quoted varname no embedded quotes - %s\n",yytext) ; - - - -/* ["][^"\\]*(?:\\.[^"\\]*)*["] { - printf("A quoted variable name - %s\n",yytext) ; - } */ - } - -"~" { - return 1 ; - } - -"\\\\\\---///" { - return 0 ; - } - -[0-9]+"."[0-9]* { - printf( "A float: %s (%g)\n", yytext, - atof( yytext ) ); - } -[a-zA-Z][a-zA-Z0-9' ']* { - printf("A variable name: %s\n",yytext) ; - } - - -"+"|"-"|"*"|"/" printf( "An operator: %s\n", yytext ); - -"{"[\^{}}\n]*"}" /* eat up one-line comments */ - -[ \t\n]+ /* eat up whitespace */ - -. printf( "Unrecognized character: %s\n", yytext ); - -%% \ No newline at end of file diff --git a/src/xmutil/third_party/xmutil/Vensim/VYacc.tab.cpp b/src/xmutil/third_party/xmutil/Vensim/VYacc.tab.cpp deleted file mode 100644 index b9987608a..000000000 --- a/src/xmutil/third_party/xmutil/Vensim/VYacc.tab.cpp +++ /dev/null @@ -1,2099 +0,0 @@ -/* A Bison parser, made by GNU Bison 3.8.2. */ - -/* Bison implementation for Yacc-like parsers in C - - Copyright (C) 1984, 1989-1990, 2000-2015, 2018-2021 Free Software Foundation, - Inc. - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . */ - -/* As a special exception, you may create a larger work that contains - part or all of the Bison parser skeleton and distribute that work - under terms of your choice, so long as that work isn't itself a - parser generator using the skeleton or a modified version thereof - as a parser skeleton. Alternatively, if you modify or redistribute - the parser skeleton itself, you may (at your option) remove this - special exception, which will cause the skeleton and the resulting - Bison output files to be licensed under the GNU General Public - License without this special exception. - - This special exception was added by the Free Software Foundation in - version 2.2 of Bison. */ - -/* C LALR(1) parser skeleton written by Richard Stallman, by - simplifying the original so-called "semantic" parser. */ - -/* DO NOT RELY ON FEATURES THAT ARE NOT DOCUMENTED in the manual, - especially those whose name start with YY_ or yy_. They are - private implementation details that can be changed or removed. */ - -/* All symbols defined below should begin with yy or YY, to avoid - infringing on user name space. This should be done even for local - variables, as they might otherwise be expanded by user macros. - There are some unavoidable exceptions within include files to - define necessary library symbols; they are noted "INFRINGES ON - USER NAME SPACE" below. */ - -/* Identify Bison output, and Bison version. */ -#define YYBISON 30802 - -/* Bison version string. */ -#define YYBISON_VERSION "3.8.2" - -/* Skeleton name. */ -#define YYSKELETON_NAME "yacc.c" - -/* Pure parsers. */ -#define YYPURE 0 - -/* Push parsers. */ -#define YYPUSH 0 - -/* Pull parsers. */ -#define YYPULL 1 - -/* Substitute the variable and function names. */ -#define yyparse vpyyparse -#define yylex vpyylex -#define yyerror vpyyerror -#define yydebug vpyydebug -#define yynerrs vpyynerrs -#define yylval vpyylval -#define yychar vpyychar - -/* First part of user prologue. */ -#line 10 "VYacc.y" - -#include "../Log.h" -#include "../Symbol/Parse.h" -#include "VensimParseFunctions.h" -extern int vpyylex(void); -extern void vpyyerror(char const *); -#define YYSTYPE ParseUnion -#define YYFPRINTF XmutilLogf - -#line 88 "VYacc.tab.cpp" - -#ifndef YY_CAST -#ifdef __cplusplus -#define YY_CAST(Type, Val) static_cast(Val) -#define YY_REINTERPRET_CAST(Type, Val) reinterpret_cast(Val) -#else -#define YY_CAST(Type, Val) ((Type)(Val)) -#define YY_REINTERPRET_CAST(Type, Val) ((Type)(Val)) -#endif -#endif -#ifndef YY_NULLPTR -#if defined __cplusplus -#if 201103L <= __cplusplus -#define YY_NULLPTR nullptr -#else -#define YY_NULLPTR 0 -#endif -#else -#define YY_NULLPTR ((void *)0) -#endif -#endif - -#include "VYacc.tab.hpp" -/* Symbol kind. */ -enum yysymbol_kind_t { - YYSYMBOL_YYEMPTY = -2, - YYSYMBOL_YYEOF = 0, /* "end of file" */ - YYSYMBOL_YYerror = 1, /* error */ - YYSYMBOL_YYUNDEF = 2, /* "invalid token" */ - YYSYMBOL_VPTT_dataequals = 3, /* VPTT_dataequals */ - YYSYMBOL_VPTT_with_lookup = 4, /* VPTT_with_lookup */ - YYSYMBOL_VPTT_map = 5, /* VPTT_map */ - YYSYMBOL_VPTT_equiv = 6, /* VPTT_equiv */ - YYSYMBOL_VPTT_groupstar = 7, /* VPTT_groupstar */ - YYSYMBOL_VPTT_and = 8, /* VPTT_and */ - YYSYMBOL_VPTT_macro = 9, /* VPTT_macro */ - YYSYMBOL_VPTT_end_of_macro = 10, /* VPTT_end_of_macro */ - YYSYMBOL_VPTT_or = 11, /* VPTT_or */ - YYSYMBOL_VPTT_not = 12, /* VPTT_not */ - YYSYMBOL_VPTT_hold_backward = 13, /* VPTT_hold_backward */ - YYSYMBOL_VPTT_look_forward = 14, /* VPTT_look_forward */ - YYSYMBOL_VPTT_except = 15, /* VPTT_except */ - YYSYMBOL_VPTT_na = 16, /* VPTT_na */ - YYSYMBOL_VPTT_interpolate = 17, /* VPTT_interpolate */ - YYSYMBOL_VPTT_raw = 18, /* VPTT_raw */ - YYSYMBOL_VPTT_test_input = 19, /* VPTT_test_input */ - YYSYMBOL_VPTT_the_condition = 20, /* VPTT_the_condition */ - YYSYMBOL_VPTT_implies = 21, /* VPTT_implies */ - YYSYMBOL_VPTT_ge = 22, /* VPTT_ge */ - YYSYMBOL_VPTT_le = 23, /* VPTT_le */ - YYSYMBOL_VPTT_ne = 24, /* VPTT_ne */ - YYSYMBOL_VPTT_tabbed_array = 25, /* VPTT_tabbed_array */ - YYSYMBOL_VPTT_eqend = 26, /* VPTT_eqend */ - YYSYMBOL_VPTT_number = 27, /* VPTT_number */ - YYSYMBOL_VPTT_literal = 28, /* VPTT_literal */ - YYSYMBOL_VPTT_symbol = 29, /* VPTT_symbol */ - YYSYMBOL_VPTT_units_symbol = 30, /* VPTT_units_symbol */ - YYSYMBOL_VPTT_function = 31, /* VPTT_function */ - YYSYMBOL_32_ = 32, /* '%' */ - YYSYMBOL_33_ = 33, /* '|' */ - YYSYMBOL_34_ = 34, /* '-' */ - YYSYMBOL_35_ = 35, /* '+' */ - YYSYMBOL_36_ = 36, /* '=' */ - YYSYMBOL_37_ = 37, /* '<' */ - YYSYMBOL_38_ = 38, /* '>' */ - YYSYMBOL_39_ = 39, /* '*' */ - YYSYMBOL_40_ = 40, /* '/' */ - YYSYMBOL_41_ = 41, /* '^' */ - YYSYMBOL_42_ = 42, /* '~' */ - YYSYMBOL_43_ = 43, /* '(' */ - YYSYMBOL_44_ = 44, /* ')' */ - YYSYMBOL_45_ = 45, /* ',' */ - YYSYMBOL_46_ = 46, /* ':' */ - YYSYMBOL_47_ = 47, /* '[' */ - YYSYMBOL_48_ = 48, /* ']' */ - YYSYMBOL_49_ = 49, /* '!' */ - YYSYMBOL_50_ = 50, /* '?' */ - YYSYMBOL_51_ = 51, /* ';' */ - YYSYMBOL_YYACCEPT = 52, /* $accept */ - YYSYMBOL_fulleq = 53, /* fulleq */ - YYSYMBOL_macrostart = 54, /* macrostart */ - YYSYMBOL_55_1 = 55, /* $@1 */ - YYSYMBOL_macroend = 56, /* macroend */ - YYSYMBOL_eqn = 57, /* eqn */ - YYSYMBOL_lhs = 58, /* lhs */ - YYSYMBOL_var = 59, /* var */ - YYSYMBOL_sublist = 60, /* sublist */ - YYSYMBOL_symlist = 61, /* symlist */ - YYSYMBOL_subdef = 62, /* subdef */ - YYSYMBOL_unitsrange = 63, /* unitsrange */ - YYSYMBOL_urangenum = 64, /* urangenum */ - YYSYMBOL_number = 65, /* number */ - YYSYMBOL_units = 66, /* units */ - YYSYMBOL_interpmode = 67, /* interpmode */ - YYSYMBOL_exceptlist = 68, /* exceptlist */ - YYSYMBOL_mapsymlist = 69, /* mapsymlist */ - YYSYMBOL_maplist = 70, /* maplist */ - YYSYMBOL_exprlist = 71, /* exprlist */ - YYSYMBOL_exp = 72, /* exp */ - YYSYMBOL_tablevals = 73, /* tablevals */ - YYSYMBOL_xytablevals = 74, /* xytablevals */ - YYSYMBOL_xytablevec = 75, /* xytablevec */ - YYSYMBOL_tablepairs = 76 /* tablepairs */ -}; -typedef enum yysymbol_kind_t yysymbol_kind_t; - -#ifdef short -#undef short -#endif - -/* On compilers that do not define __PTRDIFF_MAX__ etc., make sure - and (if available) are included - so that the code can choose integer types of a good width. */ - -#ifndef __PTRDIFF_MAX__ -#include /* INFRINGES ON USER NAME SPACE */ -#if defined __STDC_VERSION__ && 199901 <= __STDC_VERSION__ -#include /* INFRINGES ON USER NAME SPACE */ -#define YY_STDINT_H -#endif -#endif - -/* Narrow types that promote to a signed type and that can represent a - signed or unsigned integer of at least N bits. In tables they can - save space and decrease cache pressure. Promoting to a signed type - helps avoid bugs in integer arithmetic. */ - -#ifdef __INT_LEAST8_MAX__ -typedef __INT_LEAST8_TYPE__ yytype_int8; -#elif defined YY_STDINT_H -typedef int_least8_t yytype_int8; -#else -typedef signed char yytype_int8; -#endif - -#ifdef __INT_LEAST16_MAX__ -typedef __INT_LEAST16_TYPE__ yytype_int16; -#elif defined YY_STDINT_H -typedef int_least16_t yytype_int16; -#else -typedef short yytype_int16; -#endif - -/* Work around bug in HP-UX 11.23, which defines these macros - incorrectly for preprocessor constants. This workaround can likely - be removed in 2023, as HPE has promised support for HP-UX 11.23 - (aka HP-UX 11i v2) only through the end of 2022; see Table 2 of - . */ -#ifdef __hpux -#undef UINT_LEAST8_MAX -#undef UINT_LEAST16_MAX -#define UINT_LEAST8_MAX 255 -#define UINT_LEAST16_MAX 65535 -#endif - -#if defined __UINT_LEAST8_MAX__ && __UINT_LEAST8_MAX__ <= __INT_MAX__ -typedef __UINT_LEAST8_TYPE__ yytype_uint8; -#elif (!defined __UINT_LEAST8_MAX__ && defined YY_STDINT_H && UINT_LEAST8_MAX <= INT_MAX) -typedef uint_least8_t yytype_uint8; -#elif !defined __UINT_LEAST8_MAX__ && UCHAR_MAX <= INT_MAX -typedef unsigned char yytype_uint8; -#else -typedef short yytype_uint8; -#endif - -#if defined __UINT_LEAST16_MAX__ && __UINT_LEAST16_MAX__ <= __INT_MAX__ -typedef __UINT_LEAST16_TYPE__ yytype_uint16; -#elif (!defined __UINT_LEAST16_MAX__ && defined YY_STDINT_H && UINT_LEAST16_MAX <= INT_MAX) -typedef uint_least16_t yytype_uint16; -#elif !defined __UINT_LEAST16_MAX__ && USHRT_MAX <= INT_MAX -typedef unsigned short yytype_uint16; -#else -typedef int yytype_uint16; -#endif - -#ifndef YYPTRDIFF_T -#if defined __PTRDIFF_TYPE__ && defined __PTRDIFF_MAX__ -#define YYPTRDIFF_T __PTRDIFF_TYPE__ -#define YYPTRDIFF_MAXIMUM __PTRDIFF_MAX__ -#elif defined PTRDIFF_MAX -#ifndef ptrdiff_t -#include /* INFRINGES ON USER NAME SPACE */ -#endif -#define YYPTRDIFF_T ptrdiff_t -#define YYPTRDIFF_MAXIMUM PTRDIFF_MAX -#else -#define YYPTRDIFF_T long -#define YYPTRDIFF_MAXIMUM LONG_MAX -#endif -#endif - -#ifndef YYSIZE_T -#ifdef __SIZE_TYPE__ -#define YYSIZE_T __SIZE_TYPE__ -#elif defined size_t -#define YYSIZE_T size_t -#elif defined __STDC_VERSION__ && 199901 <= __STDC_VERSION__ -#include /* INFRINGES ON USER NAME SPACE */ -#define YYSIZE_T size_t -#else -#define YYSIZE_T unsigned -#endif -#endif - -#define YYSIZE_MAXIMUM \ - YY_CAST(YYPTRDIFF_T, (YYPTRDIFF_MAXIMUM < YY_CAST(YYSIZE_T, -1) ? YYPTRDIFF_MAXIMUM : YY_CAST(YYSIZE_T, -1))) - -#define YYSIZEOF(X) YY_CAST(YYPTRDIFF_T, sizeof(X)) - -/* Stored state numbers (used for stacks). */ -typedef yytype_uint8 yy_state_t; - -/* State numbers in computations. */ -typedef int yy_state_fast_t; - -#ifndef YY_ -#if defined YYENABLE_NLS && YYENABLE_NLS -#if ENABLE_NLS -#include /* INFRINGES ON USER NAME SPACE */ -#define YY_(Msgid) dgettext("bison-runtime", Msgid) -#endif -#endif -#ifndef YY_ -#define YY_(Msgid) Msgid -#endif -#endif - -#ifndef YY_ATTRIBUTE_PURE -#if defined __GNUC__ && 2 < __GNUC__ + (96 <= __GNUC_MINOR__) -#define YY_ATTRIBUTE_PURE __attribute__((__pure__)) -#else -#define YY_ATTRIBUTE_PURE -#endif -#endif - -#ifndef YY_ATTRIBUTE_UNUSED -#if defined __GNUC__ && 2 < __GNUC__ + (7 <= __GNUC_MINOR__) -#define YY_ATTRIBUTE_UNUSED __attribute__((__unused__)) -#else -#define YY_ATTRIBUTE_UNUSED -#endif -#endif - -/* Suppress unused-variable warnings by "using" E. */ -#if !defined lint || defined __GNUC__ -#define YY_USE(E) ((void)(E)) -#else -#define YY_USE(E) /* empty */ -#endif - -/* Suppress an incorrect diagnostic about yylval being uninitialized. */ -#if defined __GNUC__ && !defined __ICC && 406 <= __GNUC__ * 100 + __GNUC_MINOR__ -#if __GNUC__ * 100 + __GNUC_MINOR__ < 407 -#define YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN \ - _Pragma("GCC diagnostic push") _Pragma("GCC diagnostic ignored \"-Wuninitialized\"") -#else -#define YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN \ - _Pragma("GCC diagnostic push") _Pragma("GCC diagnostic ignored \"-Wuninitialized\"") \ - _Pragma("GCC diagnostic ignored \"-Wmaybe-uninitialized\"") -#endif -#define YY_IGNORE_MAYBE_UNINITIALIZED_END _Pragma("GCC diagnostic pop") -#else -#define YY_INITIAL_VALUE(Value) Value -#endif -#ifndef YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN -#define YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN -#define YY_IGNORE_MAYBE_UNINITIALIZED_END -#endif -#ifndef YY_INITIAL_VALUE -#define YY_INITIAL_VALUE(Value) /* Nothing. */ -#endif - -#if defined __cplusplus && defined __GNUC__ && !defined __ICC && 6 <= __GNUC__ -#define YY_IGNORE_USELESS_CAST_BEGIN _Pragma("GCC diagnostic push") _Pragma("GCC diagnostic ignored \"-Wuseless-cast\"") -#define YY_IGNORE_USELESS_CAST_END _Pragma("GCC diagnostic pop") -#endif -#ifndef YY_IGNORE_USELESS_CAST_BEGIN -#define YY_IGNORE_USELESS_CAST_BEGIN -#define YY_IGNORE_USELESS_CAST_END -#endif - -#define YY_ASSERT(E) ((void)(0 && (E))) - -#if !defined yyoverflow - -/* The parser invokes alloca or malloc; define the necessary symbols. */ - -#ifdef YYSTACK_USE_ALLOCA -#if YYSTACK_USE_ALLOCA -#ifdef __GNUC__ -#define YYSTACK_ALLOC __builtin_alloca -#elif defined __BUILTIN_VA_ARG_INCR -#include /* INFRINGES ON USER NAME SPACE */ -#elif defined _AIX -#define YYSTACK_ALLOC __alloca -#elif defined _MSC_VER -#include /* INFRINGES ON USER NAME SPACE */ -#define alloca _alloca -#else -#define YYSTACK_ALLOC alloca -#if !defined _ALLOCA_H && !defined EXIT_SUCCESS -#include /* INFRINGES ON USER NAME SPACE */ -/* Use EXIT_SUCCESS as a witness for stdlib.h. */ -#ifndef EXIT_SUCCESS -#define EXIT_SUCCESS 0 -#endif -#endif -#endif -#endif -#endif - -#ifdef YYSTACK_ALLOC -/* Pacify GCC's 'empty if-body' warning. */ -#define YYSTACK_FREE(Ptr) \ - do { /* empty */ \ - ; \ - } while (0) -#ifndef YYSTACK_ALLOC_MAXIMUM -/* The OS might guarantee only one guard page at the bottom of the stack, - and a page size can be as small as 4096 bytes. So we cannot safely - invoke alloca (N) if N exceeds 4096. Use a slightly smaller number - to allow for a few compiler-allocated temporary stack slots. */ -#define YYSTACK_ALLOC_MAXIMUM 4032 /* reasonable circa 2006 */ -#endif -#else -#define YYSTACK_ALLOC YYMALLOC -#define YYSTACK_FREE YYFREE -#ifndef YYSTACK_ALLOC_MAXIMUM -#define YYSTACK_ALLOC_MAXIMUM YYSIZE_MAXIMUM -#endif -#if (defined __cplusplus && !defined EXIT_SUCCESS && \ - !((defined YYMALLOC || defined malloc) && (defined YYFREE || defined free))) -#include /* INFRINGES ON USER NAME SPACE */ -#ifndef EXIT_SUCCESS -#define EXIT_SUCCESS 0 -#endif -#endif -#ifndef YYMALLOC -#define YYMALLOC malloc -#if !defined malloc && !defined EXIT_SUCCESS -void *malloc(YYSIZE_T); /* INFRINGES ON USER NAME SPACE */ -#endif -#endif -#ifndef YYFREE -#define YYFREE free -#if !defined free && !defined EXIT_SUCCESS -void free(void *); /* INFRINGES ON USER NAME SPACE */ -#endif -#endif -#endif -#endif /* !defined yyoverflow */ - -#if (!defined yyoverflow && (!defined __cplusplus || (defined YYSTYPE_IS_TRIVIAL && YYSTYPE_IS_TRIVIAL))) - -/* A type that is properly aligned for any stack member. */ -union yyalloc { - yy_state_t yyss_alloc; - YYSTYPE yyvs_alloc; -}; - -/* The size of the maximum gap between one aligned stack and the next. */ -#define YYSTACK_GAP_MAXIMUM (YYSIZEOF(union yyalloc) - 1) - -/* The size of an array large to enough to hold all stacks, each with - N elements. */ -#define YYSTACK_BYTES(N) ((N) * (YYSIZEOF(yy_state_t) + YYSIZEOF(YYSTYPE)) + YYSTACK_GAP_MAXIMUM) - -#define YYCOPY_NEEDED 1 - -/* Relocate STACK from its old location to the new one. The - local variables YYSIZE and YYSTACKSIZE give the old and new number of - elements in the stack, and YYPTR gives the new location of the - stack. Advance YYPTR to a properly aligned location for the next - stack. */ -#define YYSTACK_RELOCATE(Stack_alloc, Stack) \ - do { \ - YYPTRDIFF_T yynewbytes; \ - YYCOPY(&yyptr->Stack_alloc, Stack, yysize); \ - Stack = &yyptr->Stack_alloc; \ - yynewbytes = yystacksize * YYSIZEOF(*Stack) + YYSTACK_GAP_MAXIMUM; \ - yyptr += yynewbytes / YYSIZEOF(*yyptr); \ - } while (0) - -#endif - -#if defined YYCOPY_NEEDED && YYCOPY_NEEDED -/* Copy COUNT objects from SRC to DST. The source and destination do - not overlap. */ -#ifndef YYCOPY -#if defined __GNUC__ && 1 < __GNUC__ -#define YYCOPY(Dst, Src, Count) __builtin_memcpy(Dst, Src, YY_CAST(YYSIZE_T, (Count)) * sizeof(*(Src))) -#else -#define YYCOPY(Dst, Src, Count) \ - do { \ - YYPTRDIFF_T yyi; \ - for (yyi = 0; yyi < (Count); yyi++) \ - (Dst)[yyi] = (Src)[yyi]; \ - } while (0) -#endif -#endif -#endif /* !YYCOPY_NEEDED */ - -/* YYFINAL -- State number of the termination state. */ -#define YYFINAL 17 -/* YYLAST -- Last index in YYTABLE. */ -#define YYLAST 318 - -/* YYNTOKENS -- Number of terminals. */ -#define YYNTOKENS 52 -/* YYNNTS -- Number of nonterminals. */ -#define YYNNTS 25 -/* YYNRULES -- Number of rules. */ -#define YYNRULES 100 -/* YYNSTATES -- Number of states. */ -#define YYNSTATES 232 - -/* YYMAXUTOK -- Last valid token kind. */ -#define YYMAXUTOK 286 - -/* YYTRANSLATE(TOKEN-NUM) -- Symbol number corresponding to TOKEN-NUM - as returned by yylex, with out-of-bounds checking. */ -#define YYTRANSLATE(YYX) \ - (0 <= (YYX) && (YYX) <= YYMAXUTOK ? YY_CAST(yysymbol_kind_t, yytranslate[YYX]) : YYSYMBOL_YYUNDEF) - -/* YYTRANSLATE[TOKEN-NUM] -- Symbol number corresponding to TOKEN-NUM - as returned by yylex. */ -static const yytype_int8 yytranslate[] = { - 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 49, 2, 2, 2, 32, 2, 2, 43, 44, 39, 35, 45, 34, 2, 40, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 46, 51, 37, 36, 38, 50, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 47, 2, 48, 41, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 33, 2, 42, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 2, 3, 4, 5, - 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31}; - -#if YYDEBUG -/* YYRLINE[YYN] -- Source line where rule number YYN was defined. */ -static const yytype_int16 yyrline[] = { - 0, 88, 88, 89, 90, 91, 92, 93, 94, 95, 99, 99, 103, 110, 111, 112, 113, 114, 115, 116, 117, - 118, 119, 124, 125, 126, 130, 131, 135, 139, 140, 141, 142, 145, 146, 147, 148, 152, 153, 154, 155, 156, - 160, 161, 164, 165, 166, 170, 171, 172, 173, 178, 179, 180, 181, 185, 186, 190, 191, 192, 193, 198, 199, - 204, 205, 206, 207, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, 225, 226, 227, - 228, 229, 230, 231, 232, 233, 234, 235, 239, 240, 242, 247, 248, 253, 254, 259, 260}; -#endif - -/** Accessing symbol of state STATE. */ -#define YY_ACCESSING_SYMBOL(State) YY_CAST(yysymbol_kind_t, yystos[State]) - -#if YYDEBUG || 0 -/* The user-facing name of the symbol whose (internal) number is - YYSYMBOL. No bounds checking. */ -static const char *yysymbol_name(yysymbol_kind_t yysymbol) YY_ATTRIBUTE_UNUSED; - -/* YYTNAME[SYMBOL-NUM] -- String name of the symbol SYMBOL-NUM. - First, the terminals, then, starting at YYNTOKENS, nonterminals. */ -static const char *const yytname[] = {"\"end of file\"", - "error", - "\"invalid token\"", - "VPTT_dataequals", - "VPTT_with_lookup", - "VPTT_map", - "VPTT_equiv", - "VPTT_groupstar", - "VPTT_and", - "VPTT_macro", - "VPTT_end_of_macro", - "VPTT_or", - "VPTT_not", - "VPTT_hold_backward", - "VPTT_look_forward", - "VPTT_except", - "VPTT_na", - "VPTT_interpolate", - "VPTT_raw", - "VPTT_test_input", - "VPTT_the_condition", - "VPTT_implies", - "VPTT_ge", - "VPTT_le", - "VPTT_ne", - "VPTT_tabbed_array", - "VPTT_eqend", - "VPTT_number", - "VPTT_literal", - "VPTT_symbol", - "VPTT_units_symbol", - "VPTT_function", - "'%'", - "'|'", - "'-'", - "'+'", - "'='", - "'<'", - "'>'", - "'*'", - "'/'", - "'^'", - "'~'", - "'('", - "')'", - "','", - "':'", - "'['", - "']'", - "'!'", - "'?'", - "';'", - "$accept", - "fulleq", - "macrostart", - "$@1", - "macroend", - "eqn", - "lhs", - "var", - "sublist", - "symlist", - "subdef", - "unitsrange", - "urangenum", - "number", - "units", - "interpmode", - "exceptlist", - "mapsymlist", - "maplist", - "exprlist", - "exp", - "tablevals", - "xytablevals", - "xytablevec", - "tablepairs", - YY_NULLPTR}; - -static const char *yysymbol_name(yysymbol_kind_t yysymbol) { - return yytname[yysymbol]; -} -#endif - -#define YYPACT_NINF (-160) - -#define yypact_value_is_default(Yyn) ((Yyn) == YYPACT_NINF) - -#define YYTABLE_NINF (-1) - -#define yytable_value_is_error(Yyn) 0 - -/* YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing - STATE-NUM. */ -static const yytype_int16 yypact[] = { - 13, -160, -160, -160, -160, -3, 10, -160, -160, -6, 2, 203, -5, -2, -22, 26, -160, -160, - 101, 250, 25, 69, -160, -160, 20, -160, -160, -160, 52, 27, -160, -160, 89, 1, 71, 90, - -160, -160, -160, -17, -16, -25, 61, 250, -160, -160, -160, 20, 86, 250, 250, 250, 136, 170, - 141, -160, -36, 170, -160, 162, 169, 37, 154, -160, 159, 168, 153, 178, -160, 20, 250, 190, - -15, 64, -160, -160, 196, -160, 75, -160, 222, -160, -160, -160, -17, -17, -16, 214, 210, 191, - 191, 128, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, - 250, -160, -160, 223, 37, -160, -160, 37, 193, -160, 109, 227, -160, 240, 225, -160, 242, 226, - -160, -16, -160, -160, 231, -160, 129, -160, 131, 117, 211, 22, 22, 22, 191, 191, 22, 22, - 22, 214, 214, 214, 87, 170, 170, 37, 235, -160, 37, -160, 228, 236, 70, 249, -160, 92, - -16, -160, 230, -160, 243, 244, 37, 245, -160, 26, -160, 258, 260, -16, -160, 138, -160, 59, - -160, 247, 37, 97, 246, 251, 248, -16, -160, 254, 255, 264, 256, -160, 26, -160, -160, 253, - 37, 259, 261, -160, 126, -160, 257, -160, 37, -160, 37, 262, 265, 37, 271, 266, 263, 143, - 37, 268, 267, 269, 195, 31, 37, 270, 153, 178, 272, 268, 215, 178, 273, 268}; - -/* YYDEFACT[STATE-NUM] -- Default reduction number in state STATE-NUM. - Performed when YYTABLE does not specify something else to do. Zero - means the default is an error. */ -static const yytype_int8 yydefact[] = { - 0, 3, 10, 12, 2, 26, 0, 4, 5, 0, 19, 23, 0, 0, 0, 0, 27, 1, 0, 0, 14, 0, 53, 54, 0, 51, 52, 25, 24, - 0, 21, 33, 0, 61, 29, 0, 47, 9, 8, 0, 0, 0, 37, 0, 68, 67, 70, 26, 0, 0, 0, 0, 69, 18, 0, 22, 13, 63, - 44, 0, 0, 0, 0, 97, 0, 0, 95, 92, 55, 0, 0, 0, 0, 0, 20, 30, 0, 28, 0, 43, 0, 42, 7, 6, 0, 0, 0, - 87, 0, 89, 90, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 66, 45, 46, 0, 0, 15, 16, 0, - 0, 56, 0, 0, 57, 0, 62, 35, 0, 31, 50, 0, 49, 48, 0, 75, 0, 72, 0, 86, 85, 83, 81, 84, 77, 76, 88, 80, 82, - 78, 79, 91, 0, 64, 65, 0, 0, 98, 0, 11, 0, 0, 0, 0, 32, 0, 0, 73, 0, 71, 0, 0, 0, 0, 34, 0, 59, 0, - 0, 0, 40, 0, 74, 0, 99, 0, 0, 0, 0, 0, 0, 0, 38, 0, 0, 0, 0, 58, 0, 36, 41, 0, 0, 0, 0, 100, 0, - 39, 0, 17, 0, 60, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 96, 93, 0, 0, 0, 94, 0, 0}; - -/* YYPGOTO[NTERM-NUM]. */ -static const yytype_int16 yypgoto[] = {-160, -160, -160, -160, -160, -160, -160, 294, -20, -159, -160, -160, -70, - -21, -37, -160, -160, -160, -160, -19, -18, 134, -160, 96, -72}; - -/* YYDEFGOTO[NTERM-NUM]. */ -static const yytype_int8 yydefgoto[] = {0, 6, 7, 12, 8, 9, 10, 52, 16, 35, 33, 41, 80, - 81, 42, 27, 28, 122, 74, 56, 57, 64, 65, 66, 67}; - -/* YYTABLE[YYPACT[STATE-NUM]] -- What to do in state STATE-NUM. If - positive, shift that token. If negative, reduce the rule whose - number is the opposite. If YYTABLE_NINF, syntax error. */ -static const yytype_uint8 yytable[] = { - 63, 53, 78, 13, 68, 19, 72, 31, 82, 107, 17, 58, 183, 36, 120, 108, 130, 83, 59, 60, 1, 32, 2, - 3, 29, 87, 39, 30, 121, 54, 93, 89, 90, 91, 79, 202, 18, 43, 20, 4, 111, 44, 5, 14, 15, 21, - 73, 128, 129, 117, 55, 118, 45, 46, 47, 34, 48, 161, 58, 49, 50, 103, 104, 105, 58, 59, 60, 15, 51, - 132, 70, 59, 60, 134, 61, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 152, - 177, 123, 153, 93, 58, 69, 94, 172, 84, 85, 61, 59, 60, 186, 189, 124, 86, 95, 96, 97, 61, 173, 84, - 85, 62, 197, 71, 126, 75, 98, 99, 100, 101, 102, 103, 104, 105, 88, 167, 36, 166, 169, 37, 76, 93, 175, - 77, 94, 176, 193, 76, 38, 39, 220, 149, 181, 40, 225, 95, 96, 97, 155, 107, 229, 103, 104, 105, 225, 108, - 192, 98, 99, 100, 101, 102, 103, 104, 105, 207, 76, 133, 163, 164, 165, 107, 204, 93, 92, 108, 94, 108, 187, - 106, 209, 188, 210, 217, 109, 213, 218, 95, 96, 97, 219, 110, 112, 115, 93, 63, 226, 94, 113, 98, 99, 100, - 101, 102, 103, 104, 105, 114, 95, 96, 97, 22, 23, 24, 93, 25, 26, 43, 116, 119, 125, 44, 100, 101, 102, - 103, 104, 105, 95, 96, 97, 154, 45, 46, 47, 116, 48, 43, 223, 49, 50, 44, 100, 101, 102, 103, 104, 105, - 51, 131, 105, 156, 45, 46, 47, 217, 48, 43, 230, 49, 50, 44, 127, 151, 157, 158, 159, 170, 51, 178, 160, - 162, 45, 46, 47, 168, 48, 171, 174, 49, 50, 179, 184, 180, 185, 182, 191, 194, 51, 11, 195, 196, 198, 200, - 199, 201, 203, 208, 205, 206, 214, 216, 211, 0, 212, 215, 61, 221, 190, 222, 227, 228, 224, 231}; - -static const yytype_int16 yycheck[] = { - 21, 19, 39, 6, 24, 3, 5, 29, 33, 45, 0, 27, 171, 30, 29, 51, 86, 42, 34, 35, 7, 43, 9, - 10, 29, 43, 43, 29, 43, 4, 8, 49, 50, 51, 50, 194, 42, 12, 36, 26, 61, 16, 29, 46, 47, 43, - 45, 84, 85, 69, 25, 70, 27, 28, 29, 29, 31, 127, 27, 34, 35, 39, 40, 41, 27, 34, 35, 47, 43, - 88, 43, 34, 35, 92, 43, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 112, - 162, 29, 115, 8, 27, 45, 11, 29, 39, 40, 43, 34, 35, 175, 47, 43, 47, 22, 23, 24, 43, 43, 39, - 40, 47, 187, 29, 44, 49, 34, 35, 36, 37, 38, 39, 40, 41, 43, 151, 30, 45, 154, 33, 45, 8, 45, - 48, 11, 48, 44, 45, 42, 43, 217, 164, 168, 47, 221, 22, 23, 24, 44, 45, 227, 39, 40, 41, 231, 51, - 182, 34, 35, 36, 37, 38, 39, 40, 41, 44, 45, 44, 44, 45, 44, 45, 198, 8, 43, 51, 11, 51, 45, - 43, 206, 48, 208, 45, 27, 211, 48, 22, 23, 24, 216, 27, 43, 45, 8, 221, 222, 11, 44, 34, 35, 36, - 37, 38, 39, 40, 41, 44, 22, 23, 24, 13, 14, 15, 8, 17, 18, 12, 45, 34, 29, 16, 36, 37, 38, - 39, 40, 41, 22, 23, 24, 43, 27, 28, 29, 45, 31, 12, 48, 34, 35, 16, 36, 37, 38, 39, 40, 41, - 43, 44, 41, 29, 27, 28, 29, 45, 31, 12, 48, 34, 35, 16, 45, 45, 29, 45, 29, 44, 43, 44, 49, - 45, 27, 28, 29, 45, 31, 46, 34, 34, 35, 43, 29, 44, 29, 45, 44, 46, 43, 0, 44, 48, 43, 34, - 44, 44, 48, 45, 44, 43, 34, 43, 45, -1, 44, 44, 43, 45, 179, 45, 45, 44, 221, 45}; - -/* YYSTOS[STATE-NUM] -- The symbol kind of the accessing symbol of - state STATE-NUM. */ -static const yytype_int8 yystos[] = { - 0, 7, 9, 10, 26, 29, 53, 54, 56, 57, 58, 59, 55, 6, 46, 47, 60, 0, 42, 3, 36, 43, 13, 14, 15, 17, 18, 67, 68, - 29, 29, 29, 43, 62, 29, 61, 30, 33, 42, 43, 47, 63, 66, 12, 16, 27, 28, 29, 31, 34, 35, 43, 59, 72, 4, 25, 71, 72, - 27, 34, 35, 43, 47, 65, 73, 74, 75, 76, 60, 45, 43, 29, 5, 45, 70, 49, 45, 48, 66, 50, 64, 65, 33, 42, 39, 40, 47, - 72, 43, 72, 72, 72, 43, 8, 11, 22, 23, 24, 34, 35, 36, 37, 38, 39, 40, 41, 43, 45, 51, 27, 27, 65, 43, 44, 44, 45, - 45, 60, 71, 34, 29, 43, 69, 29, 43, 29, 44, 45, 66, 66, 64, 44, 71, 44, 71, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, - 72, 72, 72, 72, 72, 72, 45, 65, 65, 43, 44, 29, 29, 45, 29, 49, 64, 45, 44, 45, 44, 45, 65, 45, 65, 44, 46, 29, 43, - 34, 45, 48, 64, 44, 43, 44, 65, 45, 61, 29, 29, 64, 45, 48, 47, 73, 44, 65, 44, 46, 44, 48, 64, 43, 44, 34, 44, 61, - 48, 65, 44, 43, 44, 45, 65, 65, 45, 44, 65, 34, 44, 43, 45, 48, 65, 76, 45, 45, 48, 75, 76, 65, 45, 44, 76, 48, 45}; - -/* YYR1[RULE-NUM] -- Symbol kind of the left-hand side of rule RULE-NUM. */ -static const yytype_int8 yyr1[] = {0, 52, 53, 53, 53, 53, 53, 53, 53, 53, 55, 54, 56, 57, 57, 57, 57, 57, 57, 57, 57, - 57, 57, 58, 58, 58, 59, 59, 60, 61, 61, 61, 61, 62, 62, 62, 62, 63, 63, 63, 63, 63, - 64, 64, 65, 65, 65, 66, 66, 66, 66, 67, 67, 67, 67, 68, 68, 69, 69, 69, 69, 70, 70, - 71, 71, 71, 71, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, - 72, 72, 72, 72, 72, 72, 72, 72, 73, 73, 73, 74, 74, 75, 75, 76, 76}; - -/* YYR2[RULE-NUM] -- Number of symbols on the right-hand side of rule RULE-NUM. */ -static const yytype_int8 yyr2[] = {0, 2, 1, 1, 1, 1, 4, 4, 3, 3, 0, 6, 1, 3, 2, 4, 4, 10, 3, 1, 4, 3, 3, 1, 2, 2, - 1, 2, 3, 1, 2, 3, 4, 1, 5, 3, 7, 1, 6, 8, 5, 7, 1, 1, 1, 2, 2, 1, 3, 3, 3, 1, - 1, 1, 1, 2, 3, 1, 5, 3, 7, 0, 2, 1, 3, 3, 2, 1, 1, 1, 1, 4, 3, 4, 5, 3, 3, 3, - 3, 3, 3, 3, 3, 3, 3, 3, 3, 2, 3, 2, 2, 3, 1, 15, 17, 1, 15, 1, 3, 5, 7}; - -enum { YYENOMEM = -2 }; - -#define yyerrok (yyerrstatus = 0) -#define yyclearin (yychar = YYEMPTY) - -#define YYACCEPT goto yyacceptlab -#define YYABORT goto yyabortlab -#define YYERROR goto yyerrorlab -#define YYNOMEM goto yyexhaustedlab - -#define YYRECOVERING() (!!yyerrstatus) - -#define YYBACKUP(Token, Value) \ - do \ - if (yychar == YYEMPTY) { \ - yychar = (Token); \ - yylval = (Value); \ - YYPOPSTACK(yylen); \ - yystate = *yyssp; \ - goto yybackup; \ - } else { \ - yyerror(YY_("syntax error: cannot back up")); \ - YYERROR; \ - } \ - while (0) - -/* Backward compatibility with an undocumented macro. - Use YYerror or YYUNDEF. */ -#define YYERRCODE YYUNDEF - -/* Enable debugging if requested. */ -#if YYDEBUG - -#ifndef YYFPRINTF -#include /* INFRINGES ON USER NAME SPACE */ -#define YYFPRINTF fprintf -#endif - -#define YYDPRINTF(Args) \ - do { \ - if (yydebug) \ - YYFPRINTF Args; \ - } while (0) - -#define YY_SYMBOL_PRINT(Title, Kind, Value, Location) \ - do { \ - if (yydebug) { \ - YYFPRINTF(stderr, "%s ", Title); \ - yy_symbol_print(stderr, Kind, Value); \ - YYFPRINTF(stderr, "\n"); \ - } \ - } while (0) - -/*-----------------------------------. -| Print this symbol's value on YYO. | -`-----------------------------------*/ - -static void yy_symbol_value_print(FILE *yyo, yysymbol_kind_t yykind, YYSTYPE const *const yyvaluep) { - FILE *yyoutput = yyo; - YY_USE(yyoutput); - if (!yyvaluep) - return; - YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN - YY_USE(yykind); - YY_IGNORE_MAYBE_UNINITIALIZED_END -} - -/*---------------------------. -| Print this symbol on YYO. | -`---------------------------*/ - -static void yy_symbol_print(FILE *yyo, yysymbol_kind_t yykind, YYSTYPE const *const yyvaluep) { - YYFPRINTF(yyo, "%s %s (", yykind < YYNTOKENS ? "token" : "nterm", yysymbol_name(yykind)); - - yy_symbol_value_print(yyo, yykind, yyvaluep); - YYFPRINTF(yyo, ")"); -} - -/*------------------------------------------------------------------. -| yy_stack_print -- Print the state stack from its BOTTOM up to its | -| TOP (included). | -`------------------------------------------------------------------*/ - -static void yy_stack_print(yy_state_t *yybottom, yy_state_t *yytop) { - YYFPRINTF(stderr, "Stack now"); - for (; yybottom <= yytop; yybottom++) { - int yybot = *yybottom; - YYFPRINTF(stderr, " %d", yybot); - } - YYFPRINTF(stderr, "\n"); -} - -#define YY_STACK_PRINT(Bottom, Top) \ - do { \ - if (yydebug) \ - yy_stack_print((Bottom), (Top)); \ - } while (0) - -/*------------------------------------------------. -| Report that the YYRULE is going to be reduced. | -`------------------------------------------------*/ - -static void yy_reduce_print(yy_state_t *yyssp, YYSTYPE *yyvsp, int yyrule) { - int yylno = yyrline[yyrule]; - int yynrhs = yyr2[yyrule]; - int yyi; - YYFPRINTF(stderr, "Reducing stack by rule %d (line %d):\n", yyrule - 1, yylno); - /* The symbols being reduced. */ - for (yyi = 0; yyi < yynrhs; yyi++) { - YYFPRINTF(stderr, " $%d = ", yyi + 1); - yy_symbol_print(stderr, YY_ACCESSING_SYMBOL(+yyssp[yyi + 1 - yynrhs]), &yyvsp[(yyi + 1) - (yynrhs)]); - YYFPRINTF(stderr, "\n"); - } -} - -#define YY_REDUCE_PRINT(Rule) \ - do { \ - if (yydebug) \ - yy_reduce_print(yyssp, yyvsp, Rule); \ - } while (0) - -/* Nonzero means print parse trace. It is left uninitialized so that - multiple parsers can coexist. */ -int yydebug; -#else /* !YYDEBUG */ -#define YYDPRINTF(Args) ((void)0) -#define YY_SYMBOL_PRINT(Title, Kind, Value, Location) -#define YY_STACK_PRINT(Bottom, Top) -#define YY_REDUCE_PRINT(Rule) -#endif /* !YYDEBUG */ - -/* YYINITDEPTH -- initial size of the parser's stacks. */ -#ifndef YYINITDEPTH -#define YYINITDEPTH 200 -#endif - -/* YYMAXDEPTH -- maximum size the stacks can grow to (effective only - if the built-in stack extension method is used). - - Do not make this value too large; the results are undefined if - YYSTACK_ALLOC_MAXIMUM < YYSTACK_BYTES (YYMAXDEPTH) - evaluated with infinite-precision integer arithmetic. */ - -#ifndef YYMAXDEPTH -#define YYMAXDEPTH 10000 -#endif - -/*-----------------------------------------------. -| Release the memory associated to this symbol. | -`-----------------------------------------------*/ - -static void yydestruct(const char *yymsg, yysymbol_kind_t yykind, YYSTYPE *yyvaluep) { - YY_USE(yyvaluep); - if (!yymsg) - yymsg = "Deleting"; - YY_SYMBOL_PRINT(yymsg, yykind, yyvaluep, yylocationp); - - YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN - YY_USE(yykind); - YY_IGNORE_MAYBE_UNINITIALIZED_END -} - -/* Lookahead token kind. */ -int yychar; - -/* The semantic value of the lookahead symbol. */ -YYSTYPE yylval; -/* Number of syntax errors so far. */ -int yynerrs; - -/*----------. -| yyparse. | -`----------*/ - -int yyparse(void) { - yy_state_fast_t yystate = 0; - /* Number of tokens to shift before error messages enabled. */ - int yyerrstatus = 0; - - /* Refer to the stacks through separate pointers, to allow yyoverflow - to reallocate them elsewhere. */ - - /* Their size. */ - YYPTRDIFF_T yystacksize = YYINITDEPTH; - - /* The state stack: array, bottom, top. */ - yy_state_t yyssa[YYINITDEPTH]; - yy_state_t *yyss = yyssa; - yy_state_t *yyssp = yyss; - - /* The semantic value stack: array, bottom, top. */ - YYSTYPE yyvsa[YYINITDEPTH]; - YYSTYPE *yyvs = yyvsa; - YYSTYPE *yyvsp = yyvs; - - int yyn; - /* The return value of yyparse. */ - int yyresult; - /* Lookahead symbol kind. */ - yysymbol_kind_t yytoken = YYSYMBOL_YYEMPTY; - /* The variables used to return semantic value and location from the - action routines. */ - YYSTYPE yyval; - -#define YYPOPSTACK(N) (yyvsp -= (N), yyssp -= (N)) - - /* The number of symbols on the RHS of the reduced rule. - Keep to zero when no symbol should be popped. */ - int yylen = 0; - - YYDPRINTF((stderr, "Starting parse\n")); - - yychar = YYEMPTY; /* Cause a token to be read. */ - - goto yysetstate; - -/*------------------------------------------------------------. -| yynewstate -- push a new state, which is found in yystate. | -`------------------------------------------------------------*/ -yynewstate: - /* In all cases, when you get here, the value and location stacks - have just been pushed. So pushing a state here evens the stacks. */ - yyssp++; - -/*--------------------------------------------------------------------. -| yysetstate -- set current state (the top of the stack) to yystate. | -`--------------------------------------------------------------------*/ -yysetstate: - YYDPRINTF((stderr, "Entering state %d\n", yystate)); - YY_ASSERT(0 <= yystate && yystate < YYNSTATES); - YY_IGNORE_USELESS_CAST_BEGIN - *yyssp = YY_CAST(yy_state_t, yystate); - YY_IGNORE_USELESS_CAST_END - YY_STACK_PRINT(yyss, yyssp); - - if (yyss + yystacksize - 1 <= yyssp) -#if !defined yyoverflow && !defined YYSTACK_RELOCATE - YYNOMEM; -#else - { - /* Get the current used size of the three stacks, in elements. */ - YYPTRDIFF_T yysize = yyssp - yyss + 1; - -#if defined yyoverflow - { - /* Give user a chance to reallocate the stack. Use copies of - these so that the &'s don't force the real ones into - memory. */ - yy_state_t *yyss1 = yyss; - YYSTYPE *yyvs1 = yyvs; - - /* Each stack pointer address is followed by the size of the - data in use in that stack, in bytes. This used to be a - conditional around just the two extra args, but that might - be undefined if yyoverflow is a macro. */ - yyoverflow(YY_("memory exhausted"), &yyss1, yysize * YYSIZEOF(*yyssp), &yyvs1, yysize * YYSIZEOF(*yyvsp), - &yystacksize); - yyss = yyss1; - yyvs = yyvs1; - } -#else /* defined YYSTACK_RELOCATE */ - /* Extend the stack our own way. */ - if (YYMAXDEPTH <= yystacksize) - YYNOMEM; - yystacksize *= 2; - if (YYMAXDEPTH < yystacksize) - yystacksize = YYMAXDEPTH; - - { - yy_state_t *yyss1 = yyss; - union yyalloc *yyptr = YY_CAST(union yyalloc *, YYSTACK_ALLOC(YY_CAST(YYSIZE_T, YYSTACK_BYTES(yystacksize)))); - if (!yyptr) - YYNOMEM; - YYSTACK_RELOCATE(yyss_alloc, yyss); - YYSTACK_RELOCATE(yyvs_alloc, yyvs); -#undef YYSTACK_RELOCATE - if (yyss1 != yyssa) - YYSTACK_FREE(yyss1); - } -#endif - - yyssp = yyss + yysize - 1; - yyvsp = yyvs + yysize - 1; - - YY_IGNORE_USELESS_CAST_BEGIN - YYDPRINTF((stderr, "Stack size increased to %ld\n", YY_CAST(long, yystacksize))); - YY_IGNORE_USELESS_CAST_END - - if (yyss + yystacksize - 1 <= yyssp) - YYABORT; - } -#endif /* !defined yyoverflow && !defined YYSTACK_RELOCATE */ - - if (yystate == YYFINAL) - YYACCEPT; - - goto yybackup; - -/*-----------. -| yybackup. | -`-----------*/ -yybackup: - /* Do appropriate processing given the current state. Read a - lookahead token if we need one and don't already have one. */ - - /* First try to decide what to do without reference to lookahead token. */ - yyn = yypact[yystate]; - if (yypact_value_is_default(yyn)) - goto yydefault; - - /* Not known => get a lookahead token if don't already have one. */ - - /* YYCHAR is either empty, or end-of-input, or a valid lookahead. */ - if (yychar == YYEMPTY) { - YYDPRINTF((stderr, "Reading a token\n")); - yychar = yylex(); - } - - if (yychar <= YYEOF) { - yychar = YYEOF; - yytoken = YYSYMBOL_YYEOF; - YYDPRINTF((stderr, "Now at end of input.\n")); - } else if (yychar == YYerror) { - /* The scanner already issued an error message, process directly - to error recovery. But do not keep the error token as - lookahead, it is too special and may lead us to an endless - loop in error recovery. */ - yychar = YYUNDEF; - yytoken = YYSYMBOL_YYerror; - goto yyerrlab1; - } else { - yytoken = YYTRANSLATE(yychar); - YY_SYMBOL_PRINT("Next token is", yytoken, &yylval, &yylloc); - } - - /* If the proper action on seeing token YYTOKEN is to reduce or to - detect an error, take that action. */ - yyn += yytoken; - if (yyn < 0 || YYLAST < yyn || yycheck[yyn] != yytoken) - goto yydefault; - yyn = yytable[yyn]; - if (yyn <= 0) { - if (yytable_value_is_error(yyn)) - goto yyerrlab; - yyn = -yyn; - goto yyreduce; - } - - /* Count tokens shifted since error; after three, turn off error - status. */ - if (yyerrstatus) - yyerrstatus--; - - /* Shift the lookahead token. */ - YY_SYMBOL_PRINT("Shifting", yytoken, &yylval, &yylloc); - yystate = yyn; - YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN - *++yyvsp = yylval; - YY_IGNORE_MAYBE_UNINITIALIZED_END - - /* Discard the shifted token. */ - yychar = YYEMPTY; - goto yynewstate; - -/*-----------------------------------------------------------. -| yydefault -- do the default action for the current state. | -`-----------------------------------------------------------*/ -yydefault: - yyn = yydefact[yystate]; - if (yyn == 0) - goto yyerrlab; - goto yyreduce; - -/*-----------------------------. -| yyreduce -- do a reduction. | -`-----------------------------*/ -yyreduce: - /* yyn is the number of a rule to reduce with. */ - yylen = yyr2[yyn]; - - /* If YYLEN is nonzero, implement the default value of the action: - '$$ = $1'. - - Otherwise, the following line sets YYVAL to garbage. - This behavior is undocumented and Bison - users should not rely upon it. Assigning to YYVAL - unconditionally makes the parser a bit smaller, and it avoids a - GCC warning that YYVAL may be used uninitialized. */ - yyval = yyvsp[1 - yylen]; - - YY_REDUCE_PRINT(yyn); - switch (yyn) { - case 2: /* fulleq: VPTT_eqend */ -#line 88 "VYacc.y" - { - return VPTT_eqend; - } -#line 1317 "VYacc.tab.cpp" - break; - - case 3: /* fulleq: VPTT_groupstar */ -#line 89 "VYacc.y" - { - return VPTT_groupstar; - } -#line 1323 "VYacc.tab.cpp" - break; - - case 4: /* fulleq: macrostart */ -#line 90 "VYacc.y" - { - return '|'; - } -#line 1329 "VYacc.tab.cpp" - break; - - case 5: /* fulleq: macroend */ -#line 91 "VYacc.y" - { - return '|'; - } -#line 1335 "VYacc.tab.cpp" - break; - - case 6: /* fulleq: eqn '~' unitsrange '~' */ -#line 92 "VYacc.y" - { - vpyy_addfulleq((yyvsp[-3].eqn), (yyvsp[-1].uni)); - return '~'; - } -#line 1341 "VYacc.tab.cpp" - break; - - case 7: /* fulleq: eqn '~' unitsrange '|' */ -#line 93 "VYacc.y" - { - vpyy_addfulleq((yyvsp[-3].eqn), (yyvsp[-1].uni)); - return '|'; - } -#line 1347 "VYacc.tab.cpp" - break; - - case 8: /* fulleq: eqn '~' '~' */ -#line 94 "VYacc.y" - { - vpyy_addfulleq((yyvsp[-2].eqn), NULL); - return '~'; - } -#line 1353 "VYacc.tab.cpp" - break; - - case 9: /* fulleq: eqn '~' '|' */ -#line 95 "VYacc.y" - { - vpyy_addfulleq((yyvsp[-2].eqn), NULL); - return '|'; - } -#line 1359 "VYacc.tab.cpp" - break; - - case 10: /* $@1: %empty */ -#line 99 "VYacc.y" - { - vpyy_macro_start(); - } -#line 1365 "VYacc.tab.cpp" - break; - - case 11: /* macrostart: VPTT_macro $@1 VPTT_symbol '(' exprlist ')' */ -#line 99 "VYacc.y" - { - vpyy_macro_expression((yyvsp[-3].sym), (yyvsp[-1].exl)); - } -#line 1371 "VYacc.tab.cpp" - break; - - case 12: /* macroend: VPTT_end_of_macro */ -#line 103 "VYacc.y" - { - (yyval.tok) = (yyvsp[0].tok); - vpyy_macro_end(); - } -#line 1377 "VYacc.tab.cpp" - break; - - case 13: /* eqn: lhs '=' exprlist */ -#line 110 "VYacc.y" - { - (yyval.eqn) = vpyy_addeq((yyvsp[-2].lhs), NULL, (yyvsp[0].exl), '='); - } -#line 1383 "VYacc.tab.cpp" - break; - - case 14: /* eqn: lhs '=' */ -#line 111 "VYacc.y" - { - (yyval.eqn) = vpyy_addeq((yyvsp[-1].lhs), NULL, NULL, '='); - } -#line 1389 "VYacc.tab.cpp" - break; - - case 15: /* eqn: lhs '(' tablevals ')' */ -#line 112 "VYacc.y" - { - (yyval.eqn) = vpyy_add_lookup((yyvsp[-3].lhs), NULL, (yyvsp[-1].tbl), 0); - } -#line 1395 "VYacc.tab.cpp" - break; - - case 16: /* eqn: lhs '(' xytablevals ')' */ -#line 113 "VYacc.y" - { - (yyval.eqn) = vpyy_add_lookup((yyvsp[-3].lhs), NULL, (yyvsp[-1].tbl), 1); - } -#line 1401 "VYacc.tab.cpp" - break; - - case 17: /* eqn: lhs '=' VPTT_with_lookup '(' exp ',' '(' tablevals ')' ')' */ -#line 114 "VYacc.y" - { - (yyval.eqn) = vpyy_add_lookup((yyvsp[-9].lhs), (yyvsp[-5].exn), (yyvsp[-2].tbl), 0); - } -#line 1407 "VYacc.tab.cpp" - break; - - case 18: /* eqn: lhs VPTT_dataequals exp */ -#line 115 "VYacc.y" - { - (yyval.eqn) = vpyy_addeq((yyvsp[-2].lhs), (yyvsp[0].exn), NULL, VPTT_dataequals); - } -#line 1413 "VYacc.tab.cpp" - break; - - case 19: /* eqn: lhs */ -#line 116 "VYacc.y" - { - (yyval.eqn) = vpyy_add_lookup((yyvsp[0].lhs), NULL, NULL, 0); - } -#line 1419 "VYacc.tab.cpp" - break; - - case 20: /* eqn: VPTT_symbol ':' subdef maplist */ -#line 117 "VYacc.y" - { - (yyval.eqn) = vpyy_addeq(vpyy_addexceptinterp(vpyy_var_expression((yyvsp[-3].sym), NULL), NULL, 0), - (Expression *)vpyy_symlist_expression((yyvsp[-1].sml), (yyvsp[0].sml)), NULL, ':'); - } -#line 1425 "VYacc.tab.cpp" - break; - - case 21: /* eqn: VPTT_symbol VPTT_equiv VPTT_symbol */ -#line 118 "VYacc.y" - { - (yyval.eqn) = vpyy_addeq(vpyy_addexceptinterp(vpyy_var_expression((yyvsp[-2].sym), NULL), NULL, 0), - (Expression *)vpyy_symlist_expression(vpyy_symlist(NULL, (yyvsp[0].sym), 0, NULL), NULL), - NULL, VPTT_equiv); - } -#line 1431 "VYacc.tab.cpp" - break; - - case 22: /* eqn: lhs '=' VPTT_tabbed_array */ -#line 119 "VYacc.y" - { - (yyval.eqn) = vpyy_addeq((yyvsp[-2].lhs), (yyvsp[0].exn), NULL, '='); - } -#line 1437 "VYacc.tab.cpp" - break; - - case 23: /* lhs: var */ -#line 124 "VYacc.y" - { - (yyval.lhs) = vpyy_addexceptinterp((yyvsp[0].var), NULL, 0); - } -#line 1443 "VYacc.tab.cpp" - break; - - case 24: /* lhs: var exceptlist */ -#line 125 "VYacc.y" - { - (yyval.lhs) = vpyy_addexceptinterp((yyvsp[-1].var), (yyvsp[0].sll), 0); - } -#line 1449 "VYacc.tab.cpp" - break; - - case 25: /* lhs: var interpmode */ -#line 126 "VYacc.y" - { - (yyval.lhs) = vpyy_addexceptinterp((yyvsp[-1].var), NULL, (yyvsp[0].tok)); - } -#line 1455 "VYacc.tab.cpp" - break; - - case 26: /* var: VPTT_symbol */ -#line 130 "VYacc.y" - { - (yyval.var) = vpyy_var_expression((yyvsp[0].sym), NULL); - } -#line 1461 "VYacc.tab.cpp" - break; - - case 27: /* var: VPTT_symbol sublist */ -#line 131 "VYacc.y" - { - (yyval.var) = vpyy_var_expression((yyvsp[-1].sym), (yyvsp[0].sml)); - } -#line 1467 "VYacc.tab.cpp" - break; - - case 28: /* sublist: '[' symlist ']' */ -#line 135 "VYacc.y" - { - (yyval.sml) = (yyvsp[-1].sml); - } -#line 1473 "VYacc.tab.cpp" - break; - - case 29: /* symlist: VPTT_symbol */ -#line 139 "VYacc.y" - { - (yyval.sml) = vpyy_symlist(NULL, (yyvsp[0].sym), 0, NULL); - } -#line 1479 "VYacc.tab.cpp" - break; - - case 30: /* symlist: VPTT_symbol '!' */ -#line 140 "VYacc.y" - { - (yyval.sml) = vpyy_symlist(NULL, (yyvsp[-1].sym), 1, NULL); - } -#line 1485 "VYacc.tab.cpp" - break; - - case 31: /* symlist: symlist ',' VPTT_symbol */ -#line 141 "VYacc.y" - { - (yyval.sml) = vpyy_symlist((yyvsp[-2].sml), (yyvsp[0].sym), 0, NULL); - } -#line 1491 "VYacc.tab.cpp" - break; - - case 32: /* symlist: symlist ',' VPTT_symbol '!' */ -#line 142 "VYacc.y" - { - (yyval.sml) = vpyy_symlist((yyvsp[-3].sml), (yyvsp[-1].sym), 1, NULL); - } -#line 1497 "VYacc.tab.cpp" - break; - - case 33: /* subdef: VPTT_symbol */ -#line 145 "VYacc.y" - { - (yyval.sml) = vpyy_symlist(NULL, (yyvsp[0].sym), 0, NULL); - } -#line 1503 "VYacc.tab.cpp" - break; - - case 34: /* subdef: '(' VPTT_symbol '-' VPTT_symbol ')' */ -#line 146 "VYacc.y" - { - (yyval.sml) = vpyy_symlist(NULL, (yyvsp[-3].sym), 0, (yyvsp[-1].sym)); - } -#line 1509 "VYacc.tab.cpp" - break; - - case 35: /* subdef: subdef ',' VPTT_symbol */ -#line 147 "VYacc.y" - { - (yyval.sml) = vpyy_symlist((yyvsp[-2].sml), (yyvsp[0].sym), 0, NULL); - } -#line 1515 "VYacc.tab.cpp" - break; - - case 36: /* subdef: subdef ',' '(' VPTT_symbol '-' VPTT_symbol ')' */ -#line 148 "VYacc.y" - { - (yyval.sml) = vpyy_symlist((yyvsp[-6].sml), (yyvsp[-3].sym), 0, (yyvsp[-1].sym)); - } -#line 1521 "VYacc.tab.cpp" - break; - - case 37: /* unitsrange: units */ -#line 152 "VYacc.y" - { - (yyval.uni) = (yyvsp[0].uni); - } -#line 1527 "VYacc.tab.cpp" - break; - - case 38: /* unitsrange: units '[' urangenum ',' urangenum ']' */ -#line 153 "VYacc.y" - { - (yyval.uni) = vpyy_unitsrange((yyvsp[-5].uni), (yyvsp[-3].num), (yyvsp[-1].num), -1); - } -#line 1533 "VYacc.tab.cpp" - break; - - case 39: /* unitsrange: units '[' urangenum ',' urangenum ',' urangenum ']' */ -#line 154 "VYacc.y" - { - (yyval.uni) = vpyy_unitsrange((yyvsp[-7].uni), (yyvsp[-5].num), (yyvsp[-3].num), (yyvsp[-1].num)); - } -#line 1539 "VYacc.tab.cpp" - break; - - case 40: /* unitsrange: '[' urangenum ',' urangenum ']' */ -#line 155 "VYacc.y" - { - (yyval.uni) = vpyy_unitsrange(NULL, (yyvsp[-3].num), (yyvsp[-1].num), -1); - } -#line 1545 "VYacc.tab.cpp" - break; - - case 41: /* unitsrange: '[' urangenum ',' urangenum ',' urangenum ']' */ -#line 156 "VYacc.y" - { - (yyval.uni) = vpyy_unitsrange(NULL, (yyvsp[-5].num), (yyvsp[-3].num), (yyvsp[-1].num)); - } -#line 1551 "VYacc.tab.cpp" - break; - - case 42: /* urangenum: number */ -#line 160 "VYacc.y" - { - (yyval.num) = (yyvsp[0].num); - } -#line 1557 "VYacc.tab.cpp" - break; - - case 43: /* urangenum: '?' */ -#line 161 "VYacc.y" - { - (yyval.num) = -1e30; - } -#line 1563 "VYacc.tab.cpp" - break; - - case 44: /* number: VPTT_number */ -#line 164 "VYacc.y" - { - (yyval.num) = (yyvsp[0].num); - } -#line 1569 "VYacc.tab.cpp" - break; - - case 45: /* number: '-' VPTT_number */ -#line 165 "VYacc.y" - { - (yyval.num) = -(yyvsp[0].num); - } -#line 1575 "VYacc.tab.cpp" - break; - - case 46: /* number: '+' VPTT_number */ -#line 166 "VYacc.y" - { - (yyval.num) = (yyvsp[0].num); - } -#line 1581 "VYacc.tab.cpp" - break; - - case 47: /* units: VPTT_units_symbol */ -#line 170 "VYacc.y" - { - (yyval.uni) = (yyvsp[0].uni); - } -#line 1587 "VYacc.tab.cpp" - break; - - case 48: /* units: units '/' units */ -#line 171 "VYacc.y" - { - (yyval.uni) = vpyy_unitsdiv((yyvsp[-2].uni), (yyvsp[0].uni)); - } -#line 1593 "VYacc.tab.cpp" - break; - - case 49: /* units: units '*' units */ -#line 172 "VYacc.y" - { - (yyval.uni) = vpyy_unitsmult((yyvsp[-2].uni), (yyvsp[0].uni)); - } -#line 1599 "VYacc.tab.cpp" - break; - - case 50: /* units: '(' units ')' */ -#line 173 "VYacc.y" - { - (yyval.uni) = (yyvsp[-1].uni); - } -#line 1605 "VYacc.tab.cpp" - break; - - case 51: /* interpmode: VPTT_interpolate */ -#line 178 "VYacc.y" - { - (yyval.tok) = (yyvsp[0].tok); - } -#line 1611 "VYacc.tab.cpp" - break; - - case 52: /* interpmode: VPTT_raw */ -#line 179 "VYacc.y" - { - (yyval.tok) = (yyvsp[0].tok); - } -#line 1617 "VYacc.tab.cpp" - break; - - case 53: /* interpmode: VPTT_hold_backward */ -#line 180 "VYacc.y" - { - (yyval.tok) = (yyvsp[0].tok); - } -#line 1623 "VYacc.tab.cpp" - break; - - case 54: /* interpmode: VPTT_look_forward */ -#line 181 "VYacc.y" - { - (yyval.tok) = (yyvsp[0].tok); - } -#line 1629 "VYacc.tab.cpp" - break; - - case 55: /* exceptlist: VPTT_except sublist */ -#line 185 "VYacc.y" - { - (yyval.sll) = vpyy_chain_sublist(NULL, (yyvsp[0].sml)); - } -#line 1635 "VYacc.tab.cpp" - break; - - case 56: /* exceptlist: exceptlist ',' sublist */ -#line 186 "VYacc.y" - { - vpyy_chain_sublist((yyvsp[-2].sll), (yyvsp[0].sml)); - (yyval.sll) = (yyvsp[-2].sll); - } -#line 1641 "VYacc.tab.cpp" - break; - - case 57: /* mapsymlist: VPTT_symbol */ -#line 190 "VYacc.y" - { - (yyval.sml) = vpyy_symlist(NULL, (yyvsp[0].sym), 0, NULL); - } -#line 1647 "VYacc.tab.cpp" - break; - - case 58: /* mapsymlist: '(' VPTT_symbol ':' symlist ')' */ -#line 191 "VYacc.y" - { - (yyval.sml) = vpyy_mapsymlist(NULL, (yyvsp[-3].sym), (yyvsp[-1].sml)); - } -#line 1653 "VYacc.tab.cpp" - break; - - case 59: /* mapsymlist: mapsymlist ',' VPTT_symbol */ -#line 192 "VYacc.y" - { - (yyval.sml) = vpyy_symlist((yyvsp[-2].sml), (yyvsp[0].sym), 0, NULL); - } -#line 1659 "VYacc.tab.cpp" - break; - - case 60: /* mapsymlist: mapsymlist ',' '(' VPTT_symbol ':' symlist ')' */ -#line 193 "VYacc.y" - { - (yyval.sml) = vpyy_mapsymlist((yyvsp[-6].sml), (yyvsp[-3].sym), (yyvsp[-1].sml)); - } -#line 1665 "VYacc.tab.cpp" - break; - - case 61: /* maplist: %empty */ -#line 198 "VYacc.y" - { - (yyval.sml) = NULL; - } -#line 1671 "VYacc.tab.cpp" - break; - - case 62: /* maplist: VPTT_map mapsymlist */ -#line 199 "VYacc.y" - { - (yyval.sml) = (yyvsp[0].sml); - } -#line 1677 "VYacc.tab.cpp" - break; - - case 63: /* exprlist: exp */ -#line 204 "VYacc.y" - { - (yyval.exl) = vpyy_chain_exprlist(NULL, (yyvsp[0].exn)); - } -#line 1683 "VYacc.tab.cpp" - break; - - case 64: /* exprlist: exprlist ',' exp */ -#line 205 "VYacc.y" - { - (yyval.exl) = vpyy_chain_exprlist((yyvsp[-2].exl), (yyvsp[0].exn)); - } -#line 1689 "VYacc.tab.cpp" - break; - - case 65: /* exprlist: exprlist ';' exp */ -#line 206 "VYacc.y" - { - (yyval.exl) = vpyy_chain_exprlist((yyvsp[-2].exl), (yyvsp[0].exn)); - } -#line 1695 "VYacc.tab.cpp" - break; - - case 66: /* exprlist: exprlist ';' */ -#line 207 "VYacc.y" - { - (yyval.exl) = (yyvsp[-1].exl); - } -#line 1701 "VYacc.tab.cpp" - break; - - case 67: /* exp: VPTT_number */ -#line 211 "VYacc.y" - { - (yyval.exn) = vpyy_num_expression((yyvsp[0].num)); - } -#line 1707 "VYacc.tab.cpp" - break; - - case 68: /* exp: VPTT_na */ -#line 212 "VYacc.y" - { - (yyval.exn) = vpyy_num_expression(-1E38); - } -#line 1713 "VYacc.tab.cpp" - break; - - case 69: /* exp: var */ -#line 213 "VYacc.y" - { - (yyval.exn) = (Expression *)(yyvsp[0].var); - } -#line 1719 "VYacc.tab.cpp" - break; - - case 70: /* exp: VPTT_literal */ -#line 214 "VYacc.y" - { - (yyval.exn) = vpyy_literal_expression((yyvsp[0].lit)); - } -#line 1725 "VYacc.tab.cpp" - break; - - case 71: /* exp: var '(' exprlist ')' */ -#line 215 "VYacc.y" - { - (yyval.exn) = vpyy_lookup_expression((yyvsp[-3].var), (yyvsp[-1].exl)); - } -#line 1731 "VYacc.tab.cpp" - break; - - case 72: /* exp: '(' exp ')' */ -#line 216 "VYacc.y" - { - (yyval.exn) = vpyy_operator_expression('(', (yyvsp[-1].exn), NULL); - } -#line 1737 "VYacc.tab.cpp" - break; - - case 73: /* exp: VPTT_function '(' exprlist ')' */ -#line 217 "VYacc.y" - { - (yyval.exn) = vpyy_function_expression((yyvsp[-3].fnc), (yyvsp[-1].exl)); - } -#line 1743 "VYacc.tab.cpp" - break; - - case 74: /* exp: VPTT_function '(' exprlist ',' ')' */ -#line 218 "VYacc.y" - { - (yyval.exn) = - vpyy_function_expression((yyvsp[-4].fnc), vpyy_chain_exprlist((yyvsp[-2].exl), vpyy_literal_expression("?"))); - } -#line 1749 "VYacc.tab.cpp" - break; - - case 75: /* exp: VPTT_function '(' ')' */ -#line 219 "VYacc.y" - { - (yyval.exn) = vpyy_function_expression((yyvsp[-2].fnc), NULL); - } -#line 1755 "VYacc.tab.cpp" - break; - - case 76: /* exp: exp '+' exp */ -#line 220 "VYacc.y" - { - (yyval.exn) = vpyy_operator_expression('+', (yyvsp[-2].exn), (yyvsp[0].exn)); - } -#line 1761 "VYacc.tab.cpp" - break; - - case 77: /* exp: exp '-' exp */ -#line 221 "VYacc.y" - { - (yyval.exn) = vpyy_operator_expression('-', (yyvsp[-2].exn), (yyvsp[0].exn)); - } -#line 1767 "VYacc.tab.cpp" - break; - - case 78: /* exp: exp '*' exp */ -#line 222 "VYacc.y" - { - (yyval.exn) = vpyy_operator_expression('*', (yyvsp[-2].exn), (yyvsp[0].exn)); - } -#line 1773 "VYacc.tab.cpp" - break; - - case 79: /* exp: exp '/' exp */ -#line 223 "VYacc.y" - { - (yyval.exn) = vpyy_operator_expression('/', (yyvsp[-2].exn), (yyvsp[0].exn)); - } -#line 1779 "VYacc.tab.cpp" - break; - - case 80: /* exp: exp '<' exp */ -#line 224 "VYacc.y" - { - (yyval.exn) = vpyy_operator_expression('<', (yyvsp[-2].exn), (yyvsp[0].exn)); - } -#line 1785 "VYacc.tab.cpp" - break; - - case 81: /* exp: exp VPTT_le exp */ -#line 225 "VYacc.y" - { - (yyval.exn) = vpyy_operator_expression(VPTT_le, (yyvsp[-2].exn), (yyvsp[0].exn)); - } -#line 1791 "VYacc.tab.cpp" - break; - - case 82: /* exp: exp '>' exp */ -#line 226 "VYacc.y" - { - (yyval.exn) = vpyy_operator_expression('>', (yyvsp[-2].exn), (yyvsp[0].exn)); - } -#line 1797 "VYacc.tab.cpp" - break; - - case 83: /* exp: exp VPTT_ge exp */ -#line 227 "VYacc.y" - { - (yyval.exn) = vpyy_operator_expression(VPTT_ge, (yyvsp[-2].exn), (yyvsp[0].exn)); - } -#line 1803 "VYacc.tab.cpp" - break; - - case 84: /* exp: exp VPTT_ne exp */ -#line 228 "VYacc.y" - { - (yyval.exn) = vpyy_operator_expression(VPTT_ne, (yyvsp[-2].exn), (yyvsp[0].exn)); - } -#line 1809 "VYacc.tab.cpp" - break; - - case 85: /* exp: exp VPTT_or exp */ -#line 229 "VYacc.y" - { - (yyval.exn) = vpyy_operator_expression(VPTT_or, (yyvsp[-2].exn), (yyvsp[0].exn)); - } -#line 1815 "VYacc.tab.cpp" - break; - - case 86: /* exp: exp VPTT_and exp */ -#line 230 "VYacc.y" - { - (yyval.exn) = vpyy_operator_expression(VPTT_and, (yyvsp[-2].exn), (yyvsp[0].exn)); - } -#line 1821 "VYacc.tab.cpp" - break; - - case 87: /* exp: VPTT_not exp */ -#line 231 "VYacc.y" - { - (yyval.exn) = vpyy_operator_expression(VPTT_not, (yyvsp[0].exn), NULL); - } -#line 1827 "VYacc.tab.cpp" - break; - - case 88: /* exp: exp '=' exp */ -#line 232 "VYacc.y" - { - (yyval.exn) = vpyy_operator_expression('=', (yyvsp[-2].exn), (yyvsp[0].exn)); - } -#line 1833 "VYacc.tab.cpp" - break; - - case 89: /* exp: '-' exp */ -#line 233 "VYacc.y" - { - (yyval.exn) = vpyy_operator_expression('-', NULL, (yyvsp[0].exn)); - } -#line 1839 "VYacc.tab.cpp" - break; - - case 90: /* exp: '+' exp */ -#line 234 "VYacc.y" - { - (yyval.exn) = vpyy_operator_expression('+', NULL, (yyvsp[0].exn)); - } -#line 1845 "VYacc.tab.cpp" - break; - - case 91: /* exp: exp '^' exp */ -#line 235 "VYacc.y" - { - (yyval.exn) = vpyy_operator_expression('^', (yyvsp[-2].exn), (yyvsp[0].exn)); - } -#line 1851 "VYacc.tab.cpp" - break; - - case 92: /* tablevals: tablepairs */ -#line 239 "VYacc.y" - { - (yyval.tbl) = (yyvsp[0].tbl); - } -#line 1857 "VYacc.tab.cpp" - break; - - case 93: /* tablevals: '[' '(' number ',' number ')' '-' '(' number ',' number ')' ']' ',' tablepairs */ -#line 241 "VYacc.y" - { - (yyval.tbl) = vpyy_tablerange((yyvsp[0].tbl), (yyvsp[-12].num), (yyvsp[-10].num), (yyvsp[-6].num), (yyvsp[-4].num)); - } -#line 1863 "VYacc.tab.cpp" - break; - - case 94: /* tablevals: '[' '(' number ',' number ')' '-' '(' number ',' number ')' ',' tablepairs ']' ',' tablepairs - */ -#line 243 "VYacc.y" - { - (yyval.tbl) = vpyy_tablerange((yyvsp[0].tbl), (yyvsp[-14].num), (yyvsp[-12].num), (yyvsp[-8].num), (yyvsp[-6].num)); - } -#line 1869 "VYacc.tab.cpp" - break; - - case 95: /* xytablevals: xytablevec */ -#line 247 "VYacc.y" - { - (yyval.tbl) = (yyvsp[0].tbl); - } -#line 1875 "VYacc.tab.cpp" - break; - - case 96: /* xytablevals: '[' '(' number ',' number ')' '-' '(' number ',' number ')' ']' ',' xytablevec */ -#line 249 "VYacc.y" - { - (yyval.tbl) = vpyy_tablerange((yyvsp[0].tbl), (yyvsp[-12].num), (yyvsp[-10].num), (yyvsp[-6].num), (yyvsp[-4].num)); - } -#line 1881 "VYacc.tab.cpp" - break; - - case 97: /* xytablevec: number */ -#line 253 "VYacc.y" - { - (yyval.tbl) = vpyy_tablevec(NULL, (yyvsp[0].num)); - } -#line 1887 "VYacc.tab.cpp" - break; - - case 98: /* xytablevec: xytablevec ',' number */ -#line 254 "VYacc.y" - { - (yyval.tbl) = vpyy_tablevec((yyvsp[-2].tbl), (yyvsp[0].num)); - } -#line 1893 "VYacc.tab.cpp" - break; - - case 99: /* tablepairs: '(' number ',' number ')' */ -#line 259 "VYacc.y" - { - (yyval.tbl) = vpyy_tablepair(NULL, (yyvsp[-3].num), (yyvsp[-1].num)); - } -#line 1899 "VYacc.tab.cpp" - break; - - case 100: /* tablepairs: tablepairs ',' '(' number ',' number ')' */ -#line 260 "VYacc.y" - { - (yyval.tbl) = vpyy_tablepair((yyvsp[-6].tbl), (yyvsp[-3].num), (yyvsp[-1].num)); - } -#line 1905 "VYacc.tab.cpp" - break; - -#line 1909 "VYacc.tab.cpp" - - default: - break; - } - /* User semantic actions sometimes alter yychar, and that requires - that yytoken be updated with the new translation. We take the - approach of translating immediately before every use of yytoken. - One alternative is translating here after every semantic action, - but that translation would be missed if the semantic action invokes - YYABORT, YYACCEPT, or YYERROR immediately after altering yychar or - if it invokes YYBACKUP. In the case of YYABORT or YYACCEPT, an - incorrect destructor might then be invoked immediately. In the - case of YYERROR or YYBACKUP, subsequent parser actions might lead - to an incorrect destructor call or verbose syntax error message - before the lookahead is translated. */ - YY_SYMBOL_PRINT("-> $$ =", YY_CAST(yysymbol_kind_t, yyr1[yyn]), &yyval, &yyloc); - - YYPOPSTACK(yylen); - yylen = 0; - - *++yyvsp = yyval; - - /* Now 'shift' the result of the reduction. Determine what state - that goes to, based on the state we popped back to and the rule - number reduced by. */ - { - const int yylhs = yyr1[yyn] - YYNTOKENS; - const int yyi = yypgoto[yylhs] + *yyssp; - yystate = (0 <= yyi && yyi <= YYLAST && yycheck[yyi] == *yyssp ? yytable[yyi] : yydefgoto[yylhs]); - } - - goto yynewstate; - -/*--------------------------------------. -| yyerrlab -- here on detecting error. | -`--------------------------------------*/ -yyerrlab: - /* Make sure we have latest lookahead translation. See comments at - user semantic actions for why this is necessary. */ - yytoken = yychar == YYEMPTY ? YYSYMBOL_YYEMPTY : YYTRANSLATE(yychar); - /* If not already recovering from an error, report this error. */ - if (!yyerrstatus) { - ++yynerrs; - yyerror(YY_("syntax error")); - } - - if (yyerrstatus == 3) { - /* If just tried and failed to reuse lookahead token after an - error, discard it. */ - - if (yychar <= YYEOF) { - /* Return failure if at end of input. */ - if (yychar == YYEOF) - YYABORT; - } else { - yydestruct("Error: discarding", yytoken, &yylval); - yychar = YYEMPTY; - } - } - - /* Else will try to reuse lookahead token after shifting the error - token. */ - goto yyerrlab1; - -/*---------------------------------------------------. -| yyerrorlab -- error raised explicitly by YYERROR. | -`---------------------------------------------------*/ -yyerrorlab: - /* Pacify compilers when the user code never invokes YYERROR and the - label yyerrorlab therefore never appears in user code. */ - if (0) - YYERROR; - ++yynerrs; - - /* Do not reclaim the symbols of the rule whose action triggered - this YYERROR. */ - YYPOPSTACK(yylen); - yylen = 0; - YY_STACK_PRINT(yyss, yyssp); - yystate = *yyssp; - goto yyerrlab1; - -/*-------------------------------------------------------------. -| yyerrlab1 -- common code for both syntax error and YYERROR. | -`-------------------------------------------------------------*/ -yyerrlab1: - yyerrstatus = 3; /* Each real token shifted decrements this. */ - - /* Pop stack until we find a state that shifts the error token. */ - for (;;) { - yyn = yypact[yystate]; - if (!yypact_value_is_default(yyn)) { - yyn += YYSYMBOL_YYerror; - if (0 <= yyn && yyn <= YYLAST && yycheck[yyn] == YYSYMBOL_YYerror) { - yyn = yytable[yyn]; - if (0 < yyn) - break; - } - } - - /* Pop the current state because it cannot handle the error token. */ - if (yyssp == yyss) - YYABORT; - - yydestruct("Error: popping", YY_ACCESSING_SYMBOL(yystate), yyvsp); - YYPOPSTACK(1); - yystate = *yyssp; - YY_STACK_PRINT(yyss, yyssp); - } - - YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN - *++yyvsp = yylval; - YY_IGNORE_MAYBE_UNINITIALIZED_END - - /* Shift the error token. */ - YY_SYMBOL_PRINT("Shifting", YY_ACCESSING_SYMBOL(yyn), yyvsp, yylsp); - - yystate = yyn; - goto yynewstate; - -/*-------------------------------------. -| yyacceptlab -- YYACCEPT comes here. | -`-------------------------------------*/ -yyacceptlab: - yyresult = 0; - goto yyreturnlab; - -/*-----------------------------------. -| yyabortlab -- YYABORT comes here. | -`-----------------------------------*/ -yyabortlab: - yyresult = 1; - goto yyreturnlab; - -/*-----------------------------------------------------------. -| yyexhaustedlab -- YYNOMEM (memory exhaustion) comes here. | -`-----------------------------------------------------------*/ -yyexhaustedlab: - yyerror(YY_("memory exhausted")); - yyresult = 2; - goto yyreturnlab; - -/*----------------------------------------------------------. -| yyreturnlab -- parsing is finished, clean up and return. | -`----------------------------------------------------------*/ -yyreturnlab: - if (yychar != YYEMPTY) { - /* Make sure we have latest lookahead translation. See comments at - user semantic actions for why this is necessary. */ - yytoken = YYTRANSLATE(yychar); - yydestruct("Cleanup: discarding lookahead", yytoken, &yylval); - } - /* Do not reclaim the symbols of the rule whose action triggered - this YYABORT or YYACCEPT. */ - YYPOPSTACK(yylen); - YY_STACK_PRINT(yyss, yyssp); - while (yyssp != yyss) { - yydestruct("Cleanup: popping", YY_ACCESSING_SYMBOL(+*yyssp), yyvsp); - YYPOPSTACK(1); - } -#ifndef yyoverflow - if (yyss != yyssa) - YYSTACK_FREE(yyss); -#endif - - return yyresult; -} - -#line 266 "VYacc.y" diff --git a/src/xmutil/third_party/xmutil/Vensim/VYacc.tab.hpp b/src/xmutil/third_party/xmutil/Vensim/VYacc.tab.hpp deleted file mode 100644 index 93c295848..000000000 --- a/src/xmutil/third_party/xmutil/Vensim/VYacc.tab.hpp +++ /dev/null @@ -1,99 +0,0 @@ -/* A Bison parser, made by GNU Bison 3.8.2. */ - -/* Bison interface for Yacc-like parsers in C - - Copyright (C) 1984, 1989-1990, 2000-2015, 2018-2021 Free Software Foundation, - Inc. - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . */ - -/* As a special exception, you may create a larger work that contains - part or all of the Bison parser skeleton and distribute that work - under terms of your choice, so long as that work isn't itself a - parser generator using the skeleton or a modified version thereof - as a parser skeleton. Alternatively, if you modify or redistribute - the parser skeleton itself, you may (at your option) remove this - special exception, which will cause the skeleton and the resulting - Bison output files to be licensed under the GNU General Public - License without this special exception. - - This special exception was added by the Free Software Foundation in - version 2.2 of Bison. */ - -/* DO NOT RELY ON FEATURES THAT ARE NOT DOCUMENTED in the manual, - especially those whose name start with YY_ or yy_. They are - private implementation details that can be changed or removed. */ - -#ifndef YY_VPYY_VYACC_TAB_HPP_INCLUDED -# define YY_VPYY_VYACC_TAB_HPP_INCLUDED -/* Debug traces. */ -#ifndef YYDEBUG -# define YYDEBUG 0 -#endif -#if YYDEBUG -extern int vpyydebug; -#endif - -/* Token kinds. */ -#ifndef YYTOKENTYPE -# define YYTOKENTYPE - enum yytokentype - { - YYEMPTY = -2, - YYEOF = 0, /* "end of file" */ - YYerror = 256, /* error */ - YYUNDEF = 257, /* "invalid token" */ - VPTT_dataequals = 258, /* VPTT_dataequals */ - VPTT_with_lookup = 259, /* VPTT_with_lookup */ - VPTT_map = 260, /* VPTT_map */ - VPTT_equiv = 261, /* VPTT_equiv */ - VPTT_groupstar = 262, /* VPTT_groupstar */ - VPTT_and = 263, /* VPTT_and */ - VPTT_macro = 264, /* VPTT_macro */ - VPTT_end_of_macro = 265, /* VPTT_end_of_macro */ - VPTT_or = 266, /* VPTT_or */ - VPTT_not = 267, /* VPTT_not */ - VPTT_hold_backward = 268, /* VPTT_hold_backward */ - VPTT_look_forward = 269, /* VPTT_look_forward */ - VPTT_except = 270, /* VPTT_except */ - VPTT_na = 271, /* VPTT_na */ - VPTT_interpolate = 272, /* VPTT_interpolate */ - VPTT_raw = 273, /* VPTT_raw */ - VPTT_test_input = 274, /* VPTT_test_input */ - VPTT_the_condition = 275, /* VPTT_the_condition */ - VPTT_implies = 276, /* VPTT_implies */ - VPTT_ge = 277, /* VPTT_ge */ - VPTT_le = 278, /* VPTT_le */ - VPTT_ne = 279, /* VPTT_ne */ - VPTT_tabbed_array = 280, /* VPTT_tabbed_array */ - VPTT_eqend = 281, /* VPTT_eqend */ - VPTT_number = 282, /* VPTT_number */ - VPTT_literal = 283, /* VPTT_literal */ - VPTT_symbol = 284, /* VPTT_symbol */ - VPTT_units_symbol = 285, /* VPTT_units_symbol */ - VPTT_function = 286 /* VPTT_function */ - }; - typedef enum yytokentype yytoken_kind_t; -#endif - -/* Value type. */ - - -extern YYSTYPE vpyylval; - - -int vpyyparse (void); - - -#endif /* !YY_VPYY_VYACC_TAB_HPP_INCLUDED */ diff --git a/src/xmutil/third_party/xmutil/Vensim/VYacc.y b/src/xmutil/third_party/xmutil/Vensim/VYacc.y deleted file mode 100644 index 0807847f2..000000000 --- a/src/xmutil/third_party/xmutil/Vensim/VYacc.y +++ /dev/null @@ -1,266 +0,0 @@ -/* -c:\tools\bison\bin\win_bison -o VYacc.tab.cpp -p vpyy -d VYacc.y - - -c:\tools\bison\bin\win_bison -o $(ProjectDir)src\Vensim\VYacc.tab.cpp -p vpyy -d $(ProjectDir)src\Vensim\VYacc.y -Converting VYacc.y -Outputs: VYacc.tab.cpp VYacc.tab.hpp -*/ - -%{ -#include "../Log.h" -#include "../Symbol/Parse.h" -#include "VensimParseFunctions.h" -extern int vpyylex (void); -extern void vpyyerror (char const *); -#define YYSTYPE ParseUnion -#define YYFPRINTF XmutilLogf -%} - -/* tokens returned by the tokenizer (in addition to single char tokens) */ -%token VPTT_dataequals -%token VPTT_with_lookup -%token VPTT_map -%token VPTT_equiv -%token VPTT_groupstar -%token VPTT_and -%token VPTT_macro -%token VPTT_end_of_macro -%token VPTT_or -%token VPTT_not -%token VPTT_hold_backward -%token VPTT_look_forward -%token VPTT_except -%token VPTT_na -%token VPTT_interpolate -%token VPTT_raw -%token VPTT_test_input -%token VPTT_the_condition -%token VPTT_implies -%token VPTT_ge -%token VPTT_le -%token VPTT_ne -%token VPTT_tabbed_array -%token VPTT_eqend /* the end of equations */ - -/* tokens with types returned by the tokenizer - for these the tokenizer needs to set yylval.var - and so on before returning the token ID */ -%token VPTT_number -%token VPTT_literal -%token VPTT_symbol -%token VPTT_units_symbol -%token VPTT_function - - -/* types for the different productions - note that there are quite a few - of these and that reductions in addition to creating or reusing an object - also delete the component object to cleaning up is done as the - parsing progresses */ -%type exp -%type tablepairs tablevals xytablevals xytablevec -%type exprlist -%type lhs -%type eqn -%type units unitsrange -%type fulleq -%type var -%type symlist mapsymlist sublist subdef maplist -%type exceptlist -%type '%' '|' interpmode -%type number urangenum -%type macrostart macroend /* not really used - just sets/unsets a flag */ - - -/* precedence - low to high */ -%left '-' '+' -%left VPTT_or -%left '=' '<' '>' VPTT_le VPTT_ge VPTT_ne -%left VPTT_and -%left '*' '/' -%left VPTT_not -%right '^' /* exponentiation */ - -%% /* The grammar follows. */ - - -fulleq : - VPTT_eqend { return VPTT_eqend ; } /* finished no more to do */ - | VPTT_groupstar { return VPTT_groupstar ; } /* process the group name elsewhere */ - | macrostart { return '|'; } /* sets the context for equations that follow */ - | macroend {return '|'; } /* back to regular equations */ - | eqn '~' unitsrange '~' /* comment follows */{vpyy_addfulleq($1,$3) ; return '~' ; } - | eqn '~' unitsrange '|' /* comment skipped */ {vpyy_addfulleq($1,$3) ; return '|' ; } - | eqn '~' '~' /* units skipped */{vpyy_addfulleq($1,NULL) ; return '~' ;} - | eqn '~' '|' /* units, comment skipped */ {vpyy_addfulleq($1,NULL) ; return '|' ;} - ; - -macrostart: - VPTT_macro { vpyy_macro_start(); } VPTT_symbol '(' exprlist ')' { vpyy_macro_expression($3,$5) ;} - ; - -macroend: - VPTT_end_of_macro { $$ = $1; vpyy_macro_end(); } - ; - - - - -eqn : - lhs '=' exprlist {$$ = vpyy_addeq($1,NULL,$3,'=') ; } - | lhs '=' {$$ = vpyy_addeq($1,NULL,NULL,'=') ; } // treat as A_FUNCTION_OF nothing - | lhs '(' tablevals ')' { $$ = vpyy_add_lookup($1,NULL,$3, 0) ; } - | lhs '(' xytablevals ')' { $$ = vpyy_add_lookup($1,NULL,$3, 1) ; } - | lhs '=' VPTT_with_lookup '(' exp ',' '(' tablevals ')' ')' { $$ = vpyy_add_lookup($1,$5,$8, 0) ; } - | lhs VPTT_dataequals exp {$$ = vpyy_addeq($1,$3,NULL,VPTT_dataequals) ; } - | lhs { $$ = vpyy_add_lookup($1,NULL,NULL, 0) ; } // treat as if a lookup on time - don't have numbers - | VPTT_symbol ':' subdef maplist {$$ = vpyy_addeq(vpyy_addexceptinterp(vpyy_var_expression($1,NULL),NULL,0),(Expression *)vpyy_symlist_expression($3,$4),NULL,':') ; } - | VPTT_symbol VPTT_equiv VPTT_symbol {$$ = vpyy_addeq(vpyy_addexceptinterp(vpyy_var_expression($1,NULL),NULL,0),(Expression *)vpyy_symlist_expression(vpyy_symlist(NULL,$3,0,NULL),NULL),NULL,VPTT_equiv) ; } - | lhs '=' VPTT_tabbed_array { $$ = vpyy_addeq($1,$3,NULL,'=') ; } - ; - - -lhs : - var { $$ = vpyy_addexceptinterp($1,NULL,0) ; } - | var exceptlist {$$ = vpyy_addexceptinterp($1,$2,0) ;} - | var interpmode {$$ = vpyy_addexceptinterp($1,NULL,$2) ;} - ; - -var : - VPTT_symbol { $$ = vpyy_var_expression($1,NULL);} - | VPTT_symbol sublist { $$ = vpyy_var_expression($1,$2) ;} - ; - -sublist : - '[' symlist ']' {$$ = $2 ;} - ; - -symlist : - VPTT_symbol { $$ = vpyy_symlist(NULL,$1,0,NULL) ; } - | VPTT_symbol '!' { $$ = vpyy_symlist(NULL,$1,1,NULL) ; } - | symlist ',' VPTT_symbol { $$ = vpyy_symlist($1,$3,0,NULL) ;} - | symlist ',' VPTT_symbol '!' { $$ = vpyy_symlist($1,$3,1,NULL) ;} - ; -subdef : - VPTT_symbol { $$ = vpyy_symlist(NULL,$1,0,NULL) ; } - | '(' VPTT_symbol '-' VPTT_symbol ')' {$$ = vpyy_symlist(NULL,$2,0,$4) ;} - | subdef ',' VPTT_symbol { $$ = vpyy_symlist($1,$3,0,NULL) ; } - | subdef ',' '(' VPTT_symbol '-' VPTT_symbol ')' {$$ = vpyy_symlist($1,$4,0,$6) ; } - ; - -unitsrange : - units { $$ = $1 ; } - | units '[' urangenum ',' urangenum ']' { $$ = vpyy_unitsrange($1,$3,$5,-1) ; } - | units '[' urangenum ',' urangenum ',' urangenum ']' { $$ = vpyy_unitsrange($1,$3,$5,$7) ; } - | '[' urangenum ',' urangenum ']' { $$ = vpyy_unitsrange(NULL,$2,$4,-1) ; } - | '[' urangenum ',' urangenum ',' urangenum ']' { $$ = vpyy_unitsrange(NULL,$2,$4,$6) ; } - ; - -urangenum : - number {$$ = $1 ; } - | '?' {$$ = -1e30 ; } - ; -number : - VPTT_number {$$ = $1 ; } - | '-' VPTT_number {$$ = -$2 ;} - | '+' VPTT_number {$$ = $2 ;} - ; - -units : - VPTT_units_symbol { $$ = $1 ; } - | units '/' units {$$ = vpyy_unitsdiv($1,$3);} - | units '*' units {$$ = vpyy_unitsmult($1,$3);} - | '(' units ')' { $$ = $2 ; } /* don't record */ - ; - - -interpmode : - VPTT_interpolate { $$ = $1 ; } - | VPTT_raw { $$ = $1 ; } - | VPTT_hold_backward { $$ = $1 ; } - | VPTT_look_forward { $$ = $1 ; } - ; - -exceptlist : - VPTT_except sublist { $$ = vpyy_chain_sublist(NULL,$2) ; } - | exceptlist ',' sublist { vpyy_chain_sublist($1,$3) ; $$ = $1 ; } - ; - -mapsymlist : - VPTT_symbol { $$ = vpyy_symlist(NULL,$1,0,NULL) ; } - | '(' VPTT_symbol ':' symlist ')' { $$ = vpyy_mapsymlist(NULL, $2, $4); } - | mapsymlist ',' VPTT_symbol { $$ = vpyy_symlist($1,$3,0,NULL) ;} - | mapsymlist ',' '(' VPTT_symbol ':' symlist ')' { $$ = vpyy_mapsymlist($1, $4, $6);} - ; - - -maplist : - { $$ = NULL ; } - | VPTT_map mapsymlist { $$ = $2 ; } - ; - - // number lists can use ; to end a line -exprlist : - exp {$$ = vpyy_chain_exprlist(NULL,$1) ;} - | exprlist ',' exp {$$ = vpyy_chain_exprlist($1,$3) ; } - | exprlist ';' exp {$$ = vpyy_chain_exprlist($1,$3) ; } - | exprlist ';' {$$ = $1 ; } - ; - -exp: - VPTT_number { $$ = vpyy_num_expression($1) ; } /* since we allow unary - number not used here */ - | VPTT_na { $$ = vpyy_num_expression(-1E38);} - | var { $$ = (Expression *)$1 ; } /* ExpressionVariable is subclassed from Expression */ - | VPTT_literal { $$ = vpyy_literal_expression($1) ; } // not part of XMILE - just dumped directly for editing afterward - | var '(' exprlist ')' { $$ = vpyy_lookup_expression($1,$3) ; } - | '(' exp ')' { $$ = vpyy_operator_expression('(',$2,NULL) ; } - | VPTT_function '(' exprlist ')' { $$ = vpyy_function_expression($1,$3) ;} - | VPTT_function '(' exprlist ',' ')' { $$ = vpyy_function_expression($1,vpyy_chain_exprlist($3,vpyy_literal_expression("?"))) ;} - | VPTT_function '(' ')' { $$ = vpyy_function_expression($1,NULL) ;} - | exp '+' exp { $$ = vpyy_operator_expression('+',$1,$3) ; } - | exp '-' exp { $$ = vpyy_operator_expression('-',$1,$3) ; } - | exp '*' exp { $$ = vpyy_operator_expression('*',$1,$3) ; } - | exp '/' exp { $$ = vpyy_operator_expression('/',$1,$3) ; } - | exp '<' exp { $$ = vpyy_operator_expression('<',$1,$3) ; } - | exp VPTT_le exp { $$ = vpyy_operator_expression(VPTT_le,$1,$3) ; } - | exp '>' exp { $$ = vpyy_operator_expression('>',$1,$3) ; } - | exp VPTT_ge exp { $$ = vpyy_operator_expression(VPTT_ge,$1,$3) ; } - | exp VPTT_ne exp { $$ = vpyy_operator_expression(VPTT_ne,$1,$3) ; } - | exp VPTT_or exp { $$ = vpyy_operator_expression(VPTT_or,$1,$3) ; } - | exp VPTT_and exp { $$ = vpyy_operator_expression(VPTT_and,$1,$3) ; } - | VPTT_not exp { $$ = vpyy_operator_expression(VPTT_not,$2,NULL) ; } - | exp '=' exp { $$ = vpyy_operator_expression('=',$1,$3) ; } - | '-' exp { $$ = vpyy_operator_expression('-',NULL, $2) ; } /* unary plus - might be used by numbers */ - | '+' exp { $$ = vpyy_operator_expression('+',NULL, $2) ; } /* unary plus - might be used by numbers */ - | exp '^' exp { $$ = vpyy_operator_expression('^',$1,$3) ; } - ; - -tablevals : - tablepairs { $$ = $1 ; } - | '[' '(' number ',' number ')' '-' '(' number ',' number ')' ']' ',' tablepairs - { $$ = vpyy_tablerange($15,$3,$5,$9,$11) ; } - | '[' '(' number ',' number ')' '-' '(' number ',' number ')' ',' tablepairs ']' ',' tablepairs - { $$ = vpyy_tablerange($17,$3,$5,$9,$11) ; } - ; - - xytablevals : - xytablevec { $$ = $1 ; } - | '[' '(' number ',' number ')' '-' '(' number ',' number ')' ']' ',' xytablevec - { $$ = vpyy_tablerange($15,$3,$5,$9,$11) ; } - ; - - xytablevec : - number { $$ = vpyy_tablevec(NULL,$1) ;} - | xytablevec ',' number {$$ = vpyy_tablevec($1,$3) ;} - ; - - -tablepairs : - '(' number ',' number ')' { $$ = vpyy_tablepair(NULL,$2,$4) ;} - | tablepairs ',' '(' number ',' number ')' {$$ = vpyy_tablepair($1,$4,$6) ;} - ; - - - -/* End of grammar. */ -%% diff --git a/src/xmutil/third_party/xmutil/Vensim/VensimLex.cpp b/src/xmutil/third_party/xmutil/Vensim/VensimLex.cpp deleted file mode 100644 index 2cd7839e0..000000000 --- a/src/xmutil/third_party/xmutil/Vensim/VensimLex.cpp +++ /dev/null @@ -1,697 +0,0 @@ -/* VensimLex - - */ -#include "VensimLex.h" - -#include - -#include "VensimParse.h" -/* try to avoid the tab.h file as it is C */ -#define YYSTYPE ParseUnion -#include "../Symbol/Expression.h" -#include "../Symbol/Variable.h" -#include "../XMUtil.h" -#include "VYacc.tab.hpp" - -VensimLex::VensimLex(void) { - ucContent = NULL; - iCurPos = iFileLength = 0; - GetReady(); -} -VensimLex::~VensimLex() { -} -void VensimLex::Initialize(const char *content, off_t length) { - ucContent = content; - iFileLength = length; - iLineStart = iCurPos = 0; - iLineNumber = 1; - GetReady(); -} -int VensimLex::GetEndToken(void) { - return VPTT_eqend; -} - -void VensimLex::GetReady(void) { - iInUnitsComment = 0; - bInUnits = false; -} -std::string *VensimLex::CurToken() { - return &sToken; -} - -int VensimLex::yylex() { - int toktype = NextToken(); - switch (toktype) { - case VPTT_literal: - vpyylval.lit = sToken.c_str(); - break; - case VPTT_number: - vpyylval.num = atof(sToken.c_str()); - break; - case VPTT_symbol: - if (bInUnits) { - Units *units = VPObject->InsertUnits(sToken); - if (!units) { - // InsertUnits failed - return tilde to stop parsing - return '~'; - } - vpyylval.uni = VPObject->InsertUnitExpression(units); - toktype = VPTT_units_symbol; - break; - } - // special things here - try to do almost everything (including INTEG) as a function but some need to call out to - // different toktypes - if ((sToken[0] == 'w' || sToken[0] == 'W') && (sToken[1] == 'i' || sToken[1] == 'I') && - (sToken[2] == 't' || sToken[2] == 'T') && (sToken[3] == 'h' || sToken[3] == 'H') && - (sToken[4] == ' ' || sToken[4] == ' ') && (sToken[5] == 'l' || sToken[5] == 'L') && - (sToken[6] == 'o' || sToken[6] == 'O') && (sToken[7] == 'o' || sToken[7] == 'O') && - (sToken[8] == 'k' || sToken[8] == 'K') && (sToken[9] == 'u' || sToken[9] == 'U') && - (sToken[10] == 'p' || sToken[10] == 'P')) { - toktype = VPTT_with_lookup; - } else { - vpyylval.sym = VPObject->InsertVariable(sToken); - if (!vpyylval.sym) { - // InsertVariable failed - return tilde to stop parsing - return '~'; - } - if (vpyylval.sym->isType() == Symtype_Function) { - Function *f = static_cast(static_cast(vpyylval.sym)); - if (f->AsKeyword()) { - return ReadTabbedArray(); // todo other keywords - this will return an ExpressionNumberTable - } else - toktype = VPTT_function; - } - } - - break; - default: - break; - } - return toktype; -} - -void VensimLex::GetDigits() { - char c; - while (1) { - c = GetNextChar(true); - if (c < '0' || c > '9') { - PushBack(c, true); - break; - } - } -} - -int VensimLex::ReadTabbedArray(void) { - char c; - int row; - int toktype; - do { - c = GetNextChar(false); - } while (c && c != '(' && c != '~'); - if (c != '(') { - this->PushBack(c, false); - return c; - } - // then just numbers - tab or space separated with new lines - ExpressionNumberTable *ent = new ExpressionNumberTable(VPObject->GetSymbolNameSpace()); - row = 0; - while ((toktype = NextToken())) { - if ((toktype == '+' || toktype == '-')) { - if (NextToken() == VPTT_number) { - vpyylval.num = -vpyylval.num; - toktype = VPTT_number; - } else - throw std::runtime_error("Bad numbers"); - } - if (toktype == ')') { // finished - vpyylval.exn = ent; - return VPTT_tabbed_array; - } - if (toktype != VPTT_number) - throw "Bad numbers"; - vpyylval.num = atof(sToken.c_str()); - ent->AddValue(row, vpyylval.num); - // test for \n - while ((c = GetNextChar(false))) { - if (c == '\n') { - row++; - break; - } - if (c == '\r') { - c = GetNextChar(false); - if (c != '\n') { - PushBack(c, false); - } - row++; - break; - } - if (c != '\t' && c != '\r' && c != ' ') { - PushBack(c, false); - break; - } - } - } - return 0; -} - -int VensimLex::NextToken() // also sets token type -{ - unsigned char c; - int toktype; - - do { - c = GetNextChar(false); - } while (c == ' ' || c == '\t' || c == '\n' || c == '\r'); // consume whitespace - if (!c) { - if (sawExplicitEqEnd) { - return 0; - } - // if we're at the end of the buffer and we didn't see the marker for equations ending, - // pretend we did (a bunch of mdl files from e.g. sdeverywhere and pysd are hand-written - // as just-the-equation/no diagram files). - return GetEndToken(); - } - sToken.clear(); - PushBack(c, false); - c = GetNextChar(true); - - toktype = c; // default for many tokens - switch (c) { - case '*': // check for *** which names groups - we are dropping group info for now - if (TestTokenMatch("**", false)) { - // look for - // **** - // groupname.nested.with.d - // **** - sToken.clear(); - while ((c = GetNextChar(false)) == '*') - ; - while (c == '\r' || c == '\n' || c == ' ' || c == '\t') - c = GetNextChar(false); - do { - this->sToken.push_back(c); - if (c == '.') { - sToken.pop_back(); - if (!sToken.empty()) - sToken.push_back('-'); // can't use . in a module name - } - c = GetNextChar(false); - } while (c != '\r' && c != '\n' && c != ' ' && c != '\t'); - while ((c = GetNextChar(false)) != '*' && c != '|') - ; - while (c && c != '|') - c = GetNextChar(false); - return VPTT_groupstar; - } - break; - // single character tokens - case '~': - if (iInUnitsComment == 0) - bInUnits = true; - iInUnitsComment++; - break; - case '=': // := is handled by : - if (TestTokenMatch("=", true)) - return '='; // we ignore the invariant == that Vensim supports - break; - case '/': - // either / or ///---\\\ but skip last \ because continuation line chars will mess thigns up xxx - if (TestTokenMatch("//---\\\\", false)) { - assert(!sBuffer.length()); - sBuffer = "///---\\\\"; - iCurPos--; // back up - wont be a problem with continuation lines in this case - sawExplicitEqEnd = true; - return VPTT_eqend; // finished normal parse - } - break; - case '^': - break; - case '!': - break; - case '(': - break; - case ')': - break; - case '}': - break; - case '[': - if (iInUnitsComment) - bInUnits = false; - case ']': - break; - case '|': - break; - case ',': - break; - case '+': - break; - case '>': - if (TestTokenMatch("=", true)) - return VPTT_ge; - break; - case '-': - if (TestTokenMatch(">", true)) - return VPTT_map; - break; - case '<': - if (TestTokenMatch("->", true)) - return VPTT_equiv; - if (TestTokenMatch("=", true)) - return VPTT_le; - if (TestTokenMatch(">", true)) - return VPTT_ne; - break; - case '1': - if (bInUnits) { - Units *units = VPObject->InsertUnits("1"); - if (!units) { - // InsertUnits failed - return tilde to stop parsing - return '~'; - } - vpyylval.uni = VPObject->InsertUnitExpression(units); - return VPTT_units_symbol; - } - /* fallthrough */ - case '.': // maybe a number check next digit - case '0': - - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - case '8': - case '9': - // a number follow it through till it is no longer a number - if (c == '.') { - c = GetNextChar(true); - if (c < '0' || c > '9') { - PushBack(c, true); - break; // not a number return '.' - } - GetDigits(); - } else { - GetDigits(); - c = GetNextChar(true); - if (c != '.') - PushBack(c, true); - } - toktype = VPTT_number; - if (c == '.') - GetDigits(); - c = GetNextChar(true); - if (c == 'E' || c == 'e') { // xxx.xxxE+-xx - c = GetNextChar(true); - if (c != '+' && c != '-') - PushBack(c, true); - GetDigits(); - } else - PushBack(c, true); - break; - case ':': // := :AND: :HOLD BACKWARD: :IMPLIES: :INTERPOLATE: :LOOK FORWARD: :OR: :NA: :NOT: :RAW: :TEST INPUT: - // :THE CONDITION: - c = GetNextChar(true); - if (c == '=') - return VPTT_dataequals; - PushBack(c, true); - return TestColonKeyword(); // might return ':' - case '{': // a comment, find matching } - { // nesting, len local in scope - int nesting = 1; - int len = 1; - MarkPosition(); - while ((c = GetNextChar(false))) { - len++; - if (len > 1028) - break; // excessive comments not considered valid - if (c == '}') { - nesting--; - if (!nesting) { // comment is in sToken right now - return NextToken(); // just skip the comment for now - as if not there - } - } else if (c == '{') - nesting++; - else if (c == '*' && nesting == 1) { - c = GetNextChar(false); - if (c == '*') // treat this as a group - from the original dynamo format - { - while ((c = GetNextChar(false)) == '*') - ; - while (c == '\r' || c == '\n' || c == ' ' || c == '\t') - c = GetNextChar(false); - if (c == '}') - return NextToken(); // no useful group name - sToken.clear(); - do { - this->sToken.push_back(c); - if (c == '.') { - sToken.pop_back(); - if (!sToken.empty()) - sToken.push_back('-'); // can't use . in a module name - } - c = GetNextChar(false); - } while (c != '\r' && c != '\n' && c != '*' && c != '}'); - while (sToken.back() == ' ') - sToken.pop_back(); - while (c && c != '}') - c = GetNextChar(false); - return VPTT_groupstar; - } else - PushBack(c, false); - } - } - ReturnToMark(); // failed to find pair give up - } break; // give up and just return the one char - will throw error message - case '\'': // vensim literal - just look for matching ' - { - int len; - for (len = 1; (c = GetNextChar(true)); len++) { - if (c == '\'') { - return VPTT_literal; // the returned token includes both the opening and closing quote - } - } - } break; - case '\"': // a quoted variable name potentially with embedded escaped quotes - { - int len; - MarkPosition(); - for (len = 1; (c = GetNextChar(true)); len++) { - if (c == '\"') { - return VPTT_symbol; // the returned token includes both the opening and closing quote - } else if (c == '\\') { // skip what follows in case it is a " or \ -- - GetNextChar(true); - len++; - } - if (len > 1024) // apparently unmmatched - break; - } - } - ReturnToMark(); - break; // give up and just return the one char - case '\\': // either \\\---/// or a continuation line or an error - if (TestTokenMatch("\\\\---///", false)) { - assert(!sBuffer.length()); - sBuffer = "\\\\\\---///"; - iCurPos--; // back up - wont be a problem with continuation lines in this case - sawExplicitEqEnd = true; - return VPTT_eqend; // finished normal parse - } - break; - default: // a variable name or an unrecognizable token - if (c == 'G' || c == 'g') { - // the GET XLS functins don't really translat so we return 0 and the entire expression as a comment - if (IsGetXLSorVDF()) - return VPTT_symbol; - } - if (isalpha(c) || c > 127 || ((iInUnitsComment == 1) && c == '$')) { // a variable - while ((c = GetNextChar(true))) { - if (!isalnum(c) && c != ' ' && c != '_' && c != '$' && c != '\t' && c != '\'' && c < 128) { - PushBack(c, true); - break; - } - } - // strip any terminal spaces - while (sToken.back() == ' ' || sToken.back() == '_') - sToken.pop_back(); - return VPTT_symbol; - } - } - return toktype; -} - -int VensimLex::TestColonKeyword() { - // := :AND: :HOLD BACKWARD: :IMPLIES: :INTERPOLATE: :LOOK FORWARD: :OR: :NA: :NOT: :RAW: :TEST INPUT: :THE - // CONDITION: - - const char *keywords[] = { - ":AND:", ":END OF MACRO:", ":EXCEPT:", ":HOLD BACKWARD:", ":IMPLIES:", ":INTERPOLATE:", ":LOOK FORWARD:", - ":MACRO:", ":OR:", ":NA:", ":NOT:", ":RAW:", ":TESTINPUT:", ":THECONDITION:", - NULL}; - - int keyvals[] = {VPTT_and, - VPTT_end_of_macro, - VPTT_except, - VPTT_hold_backward, - VPTT_implies, - VPTT_interpolate, - VPTT_look_forward, - VPTT_macro, - VPTT_or, - VPTT_na, - VPTT_not, - VPTT_raw, - VPTT_test_input, - VPTT_the_condition, - -1}; - int i; - char c = GetNextChar(true); - for (i = 0; keywords[i]; i++) { - if (toupper(c) == keywords[i][1]) { - if (KeywordMatch(keywords[i] + 2)) - return keyvals[i]; - } - } - PushBack(c, true); - return ':'; -} - -// treat the GET XLS functions as varialbles to make it easier to figure out what to do on the other side -bool VensimLex::IsGetXLSorVDF() { - // Vensim has a bunch of GET functions that use strings and aren't worth translating - if (KeywordMatch("ET 123")) - sToken = "{GET 123"; - else if (KeywordMatch("ET DATA")) - sToken = "{GET DATA"; - else if (KeywordMatch("ET DIRECT")) - sToken = "{GET DIRECT"; - else if (KeywordMatch("ET VDF")) - sToken = "{GET VDF"; - else if (KeywordMatch("ET XLS")) - sToken = "{GET XLS"; - else - return false; - char c; - while ((c = GetNextChar(true))) { - if (c == '(') - break; - } - int nesting = 1; - while (nesting && (c = GetNextChar(true))) { - if (c == '(') - nesting++; - else if (c == ')') - nesting--; - } - sToken.push_back('}'); - return true; -} - -// target assumed upper case -bool VensimLex::KeywordMatch(const char *target) { - std::string buffer; - char c; - int i; - for (i = 0; target[i]; i++) { - c = GetNextChar(true); - if (target[i] == ' ') { /* one or more _ or space ok here */ - if (c != ' ' && c != '_' && c != '\t') - break; - buffer.push_back(c); - while ((c = GetNextChar(true))) { - if (c != ' ' && c != '_' && c != '\t') - break; - buffer.push_back(c); - } - PushBack(c, true); - } else if (toupper(c) != target[i]) - break; - else - buffer.push_back(c); - } - if (target[i]) { // not a match - PushBack(c, true); // last one taken should be sent back - while (buffer.length()) { - PushBack(buffer.back(), true); - buffer.pop_back(); - } - return false; - } - return true; -} - -char VensimLex::GetNextChar(bool store) { - char c; - if (sBuffer.length()) { - c = sBuffer.back(); - sBuffer.pop_back(); - if (store) - sToken.push_back(c); - return c; - } - if (iCurPos >= iFileLength) - return 0; // nothing to do - c = ucContent[iCurPos++]; - if (c == '\\') { // check for continuation lines - if (iCurPos < iFileLength && (ucContent[iCurPos] == '\n' || ucContent[iCurPos] == '\r')) { - for (; iCurPos < iFileLength;) { - c = ucContent[iCurPos++]; - if (c == '\n') { - iLineNumber++; - iLineStart = iCurPos + 1; // actually the next pos - } else if (c != '\t' && c != ' ' && c != '\r') - break; - // note as in vensim two \ line ends in a row just cause an error - } - } - } else if (c == '\n') { - iLineNumber++; - iLineStart = iCurPos; // actually the next pos - } - if (store) - sToken.push_back(c); - return c; -} - -// check for token match - advance position on success -bool VensimLex::TestTokenMatch(const char *tok, bool storeonsuccess) { - char c; - if (!*tok) - return true; - int i; - for (i = 0; tok[i]; i++) { - if ((c = GetNextChar(storeonsuccess)) != tok[i]) - break; - } - if (tok[i]) { - PushBack(c, storeonsuccess); - while (i-- > 0) - PushBack(tok[i], storeonsuccess); - return false; - } - return true; -} - -std::string VensimLex::GetComment(const char *tok) { - char c; - std::string rval; - _lastCommentHadSupplementary = false; - - while ((c = GetNextChar(false))) { - if (c == *tok && TestTokenMatch(tok + 1, true)) { - PushBack(c, false); // next call to findToken will find this - // strip trailing white space - while (!rval.empty()) { - c = rval.back(); - if (c != ' ' && c != '\t' && c != '\r' && c != '\n') - break; - rval.pop_back(); - } - - // Check for supplementary flag: ~ :SUP or ~ :SUPPLEMENTARY at end - size_t tildePos = rval.rfind('~'); - if (tildePos != std::string::npos) { - std::string afterTilde = rval.substr(tildePos + 1); - // Trim leading whitespace - size_t start = afterTilde.find_first_not_of(" \t\r\n"); - if (start != std::string::npos) { - afterTilde = afterTilde.substr(start); - } - // Convert to uppercase for case-insensitive comparison - std::string upper = afterTilde; - for (char &ch : upper) { - ch = toupper(ch); - } - if (upper == ":SUP" || upper == ":SUPPLEMENTARY") { - _lastCommentHadSupplementary = true; - // Strip the supplementary flag from the comment - rval = rval.substr(0, tildePos); - // Trim trailing whitespace from remaining comment - while (!rval.empty()) { - c = rval.back(); - if (c != ' ' && c != '\t' && c != '\r' && c != '\n') - break; - rval.pop_back(); - } - } - } - - return rval; - } else if (c == '\\' && TestTokenMatch("\\\\---///", false)) { - PushBack(c, false); - SyncBuffers(); - return rval; // an error - } - rval.push_back(c); - } - return rval; // this is an error condition -} - -bool VensimLex::FindToken(const char *tok) { - char c; - while ((c = GetNextChar(false))) { - if (c == *tok && TestTokenMatch(tok + 1, true)) - return true; - else if (c == '\\' && TestTokenMatch("\\\\---///", false)) { - PushBack(c, false); - SyncBuffers(); - return false; - } else if (c == '/') - if (TestTokenMatch("//---\\\\", false)) { - PushBack(c, false); - SyncBuffers(); - return false; - } - } - return false; -} - -bool VensimLex::BufferReadLine(char *buf, size_t buflen) { - const char *tv = sBuffer.c_str(); - while (buflen > 0 && *tv) { - *buf = *tv; - buf++; - buflen--; - tv++; - } - return ReadLine(buf, buflen); -} -bool VensimLex::ReadLine(char *buf, size_t buflen) { - char c; - buflen--; // need \0 - size_t off = 0; - while (iCurPos < iFileLength) { - c = ucContent[iCurPos++]; - if (off >= buflen) { - iCurPos--; - buf[off] = '\0'; - return true; - } - if (c == '\n') { - buf[off] = '\0'; - if (iCurPos < iFileLength && ucContent[iCurPos] == '\r') - iCurPos++; - return true; - } - if (c == '\r') { - buf[off] = '\0'; - if (iCurPos < iFileLength && ucContent[iCurPos] == '\n') - iCurPos++; - return true; - } - buf[off++] = c; - } - buf[0] = '\0'; - return false; -} - -void VensimLex::PushBack(char c, bool store) { - assert(c); - sBuffer.push_back(c); - if (store) - sToken.pop_back(); -} -void VensimLex::SyncBuffers(void) { - int i = sBuffer.length(); - iCurPos -= i; - sBuffer.clear(); -} diff --git a/src/xmutil/third_party/xmutil/Vensim/VensimLex.h b/src/xmutil/third_party/xmutil/Vensim/VensimLex.h deleted file mode 100644 index 67147afab..000000000 --- a/src/xmutil/third_party/xmutil/Vensim/VensimLex.h +++ /dev/null @@ -1,67 +0,0 @@ -#ifndef _XMUTIL_VENSIM_VENSIMLEX_H -#define _XMUTIL_VENSIM_VENSIMLEX_H - -/* a tokenizer for Vensim files - it is called by VensimPare both - indirectly throught the Bison generated parser and directly for - comments and group defs */ -#include -#include - -#include "../Symbol/Parse.h" - -class VensimLex { -public: - VensimLex(void); - ~VensimLex(void); - void Initialize(const char *content, off_t length); - std::string *CurToken(void); - void GetReady(void); - int yylex(void); - int GetEndToken(void); - int LineNumber(void) { - return iLineNumber; - } - int Position(void) { - return iCurPos - iLineStart; - } - std::string GetComment(const char *tok); - bool LastCommentHadSupplementary() const { - return _lastCommentHadSupplementary; - } - bool FindToken(const char *tok); - bool BufferReadLine(char *buf, size_t buflen); // start with buffer then read the line - bool ReadLine(char *buf, size_t buflen); // read a line if enough room otherwise part of it -private: - char GetNextChar(bool store); - void PushBack(char c, bool store); - void SyncBuffers(void); - bool TestTokenMatch(const char *tok, bool update); - std::string sToken; - std::string sBuffer; - const char *ucContent; - off_t iCurPos, iHoldPos; - off_t iLineStart, iHoldStart; - void MarkPosition(void) { - iHoldPos = iCurPos; - iHoldStart = iLineStart; - } - void ReturnToMark(void) { - iCurPos = iHoldPos; - iLineStart = iHoldStart; - sBuffer.clear(); - } - int iLineNumber; - off_t iFileLength; - int NextToken(void); - bool IsGetXLSorVDF(void); - bool KeywordMatch(const char *target); - void GetDigits(void); - int iInUnitsComment; // 0 no, 1 units, 2 comment - int TestColonKeyword(void); - int ReadTabbedArray(void); - bool bInUnits; - bool sawExplicitEqEnd = false; - bool _lastCommentHadSupplementary = false; -}; - -#endif diff --git a/src/xmutil/third_party/xmutil/Vensim/VensimParse.cpp b/src/xmutil/third_party/xmutil/Vensim/VensimParse.cpp deleted file mode 100644 index 93ad3d414..000000000 --- a/src/xmutil/third_party/xmutil/Vensim/VensimParse.cpp +++ /dev/null @@ -1,701 +0,0 @@ -// VensimParse.cpp : Read an mdl file into an XModel object -// we use an in-memory string to simplify look ahead/back -// we include the tokenizer here because it is as easy as setting -// up regular expressions for Flex and more easily understood - -#include "VensimParse.h" - -#include - -#include "../Symbol/ExpressionList.h" -#include "../Symbol/LeftHandSide.h" -#include "../Symbol/Variable.h" -#define YYSTYPE VensimParse -#include "../XMUtil.h" -#include "VYacc.tab.hpp" -#include "VensimView.h" - -VensimParse *VPObject = NULL; - -VensimParse::VensimParse(Model *model) { -#if YYDEBUG - vpyydebug = 0; -#endif - assert(!VPObject); - VPObject = this; - _model = model; - pSymbolNameSpace = model->GetNameSpace(); - bLongName = false; - ReadyFunctions(); -} -VensimParse::~VensimParse(void) { - VPObject = NULL; -} - -void VensimParse::ReadyFunctions() { - // initialize functions - actually need to split this up for common functions - // and those specific to Vensim - later - try { - new FunctionMin(pSymbolNameSpace); - new FunctionMax(pSymbolNameSpace); - new FunctionInteg(pSymbolNameSpace); - new FunctionActiveInitial(pSymbolNameSpace); - new FunctionInitial(pSymbolNameSpace); - new FunctionReInitial(pSymbolNameSpace); - new FunctionSampleIfTrue(pSymbolNameSpace); - new FunctionPulse(pSymbolNameSpace); - new FunctionPulseTrain(pSymbolNameSpace); - new FunctionQuantum(pSymbolNameSpace); - new FunctionIfThenElse(pSymbolNameSpace); - new FunctionLog(pSymbolNameSpace); - new FunctionZidz(pSymbolNameSpace); - new FunctionXidz(pSymbolNameSpace); - new FunctionLookupInv(pSymbolNameSpace); - new FunctionWithLookup(pSymbolNameSpace); // but WITH_LOOKUP is treated specially by parser - new FunctionStep(pSymbolNameSpace); - new FunctionTabbedArray(pSymbolNameSpace); - new FunctionRamp(pSymbolNameSpace); - new FunctionLn(pSymbolNameSpace); - new FunctionSmooth(pSymbolNameSpace); - new FunctionSmoothI(pSymbolNameSpace); - new FunctionSmooth3(pSymbolNameSpace); - new FunctionSmooth3I(pSymbolNameSpace); - new FunctionTrend(pSymbolNameSpace); - new FunctionFrcst(pSymbolNameSpace); - new FunctionDelay1(pSymbolNameSpace); - new FunctionDelay1I(pSymbolNameSpace); - new FunctionDelay3(pSymbolNameSpace); - new FunctionDelay3I(pSymbolNameSpace); - new FunctionDelay(pSymbolNameSpace); - new FunctionDelayN(pSymbolNameSpace); - new FunctionSmoothN(pSymbolNameSpace); - new FunctionDelayConveyor(pSymbolNameSpace); - new FunctionVectorReorder(pSymbolNameSpace); - new FunctionVectorLookup(pSymbolNameSpace); - new FunctionElmCount(pSymbolNameSpace); - new FunctionRandomNormal(pSymbolNameSpace); - new FunctionRandomPoisson(pSymbolNameSpace); - new FunctionLookupArea(pSymbolNameSpace); - new FunctionLookupExtrapolate(pSymbolNameSpace); - new FunctionLookupForward(pSymbolNameSpace); - new FunctionLookupBackward(pSymbolNameSpace); - new FunctionGetDataAtTime(pSymbolNameSpace); - new FunctionGetDataLastTime(pSymbolNameSpace); - new FunctionModulo(pSymbolNameSpace); - new FunctionNPV(pSymbolNameSpace); - new FunctionSum(pSymbolNameSpace); - new FunctionProd(pSymbolNameSpace); - new FunctionVMax(pSymbolNameSpace); - new FunctionVMin(pSymbolNameSpace); - new FunctionTimeBase(pSymbolNameSpace); - new FunctionVectorSelect(pSymbolNameSpace); - new FunctionVectorElmMap(pSymbolNameSpace); - new FunctionVectorSortOrder(pSymbolNameSpace); - new FunctionGame(pSymbolNameSpace); - new FunctionRandom01(pSymbolNameSpace); - new FunctionRandomUniform(pSymbolNameSpace); - new FunctionRandomPink(pSymbolNameSpace); - new FunctionAbs(pSymbolNameSpace); - new FunctionExp(pSymbolNameSpace); - new FunctionSqrt(pSymbolNameSpace); - new FunctionNAN(pSymbolNameSpace); - - new FunctionCosine(pSymbolNameSpace); - new FunctionSine(pSymbolNameSpace); - new FunctionTangent(pSymbolNameSpace); - new FunctionArcCosine(pSymbolNameSpace); - new FunctionArcSine(pSymbolNameSpace); - new FunctionArcTangent(pSymbolNameSpace); - new FunctionInterger(pSymbolNameSpace); - - new FunctionGetDirectData(pSymbolNameSpace); - new FunctionGetDataMean(pSymbolNameSpace); - - new FunctionAllocateByPriority(pSymbolNameSpace); - - pSymbolNameSpace->ConfirmAllAllocations(); - } catch (...) { - log("Failed to initialize symbol table"); - } -} -Equation *VensimParse::AddEq(LeftHandSide *lhs, Expression *ex, ExpressionList *exl, int tok) { - if (exl) { - if (exl->Length() == 1) { - ex = exl->GetExp(0); - delete exl; - } else { /* only a list of numbers is valid here - throw exception for anything else */ - ExpressionNumberTable *ent = new ExpressionNumberTable(pSymbolNameSpace); - int n = exl->Length(); - int i; - for (i = 0; i < n; i++) { - ex = exl->GetExp(i); - if (ex->GetType() == EXPTYPE_Operator && ex->GetArg(0) == NULL && *ex->GetOperator() == '-') { - // alloe unary minus here - ent->AddValue(0, -ex->GetArg(1)->Eval(NULL)); // note eval does not need context for number - } else if (ex->GetType() != EXPTYPE_Number) { - has_error_ = true; - last_error_ = "Expecting only comma delimited numbers "; - delete ex; // Delete the current expression that caused the error - delete ent; - delete exl; - return nullptr; - } else - ent->AddValue(0, ex->Eval(NULL)); // note eval does not need context for number - delete ex; - } - delete exl; - ex = ent; - } - } - return new Equation(pSymbolNameSpace, lhs, ex, tok); -} -Equation *VensimParse::AddTable(LeftHandSide *lhs, Expression *ex, ExpressionTable *tbl, bool legacy) { - if (!tbl) { - // this is an exogenous data entry - treat as a table on time and give the table value 1 at all times if we have - // that info - tbl = new ExpressionTable(this->pSymbolNameSpace); - tbl->AddPair(0, 1); - tbl->AddPair(1, 1); - Variable *var = this->FindVariable("TIME"); - if (!var) - var = new Variable(this->pSymbolNameSpace, "TIME"); - ex = new ExpressionVariable(this->pSymbolNameSpace, var, NULL); - } - if (legacy) - tbl->TransformLegacy(); - if (!ex) - return new Equation(pSymbolNameSpace, lhs, tbl, '('); - // with lookup is just a table embedded in a variable - actually the norm for XMILE - // Function* wl = static_cast(pSymbolNameSpace->Find("WITH LOOKUP")); - Expression *rhs = new ExpressionLookup(pSymbolNameSpace, ex, tbl); - return new Equation(pSymbolNameSpace, lhs, rhs, '='); -} - -/* a full eq cleans up the temporary memory space and - also assigns the equation to the Variable - not that inside - of macros there is a separate symbol space this is going - against */ -void VensimParse::AddFullEq(Equation *eq, UnitExpression *un) { - pSymbolNameSpace->ConfirmAllAllocations(); // now independently allocated - pActiveVar = eq->GetVariable(); - if (!_model->Groups().empty() && pActiveVar->GetAllEquations().empty() && !mInMacro) { - _model->Groups().back()->vVariables.push_back(pActiveVar); - pActiveVar->SetGroup(_model->Groups().back()); - } - pActiveVar->AddEq(eq); - if (un) { - if (!pActiveVar->AddUnits(un)) - delete un; - } -} - -int VensimParse::yyerror(const char *str) { - // Set error state instead of throwing exception - has_error_ = true; - last_error_ = str; - // Return 1 to signal error to the parser - return 1; -} - -static std::string compress_whitespace(const std::string &s) { - std::string rval; - const char *tv = s.c_str(); - for (; *tv; tv++) { - if (*tv != ' ' && *tv != '\t' && *tv != '\n' && *tv != '\r') - break; - } - for (; *tv; tv++) { - if (*tv == '~') - break; // some comments have supplementary in them - if (*tv == ' ' || *tv == '\t' || *tv == '\n' || *tv == '\r') { - rval.push_back('_'); - for (; tv[1]; tv++) { - if (tv[1] != ' ' && tv[1] != '\t' && tv[1] != '\n' && tv[1] != '\r') - break; - } - } else if ((*tv >= 'A' && *tv <= 'Z') || (*tv >= 'a' && *tv <= 'z')) - rval.push_back(*tv); // otherwise ignore - } - while (rval.back() == '_') - rval.pop_back(); - return rval; -} - -bool VensimParse::ProcessFile(const std::string &filename, const char *contents, size_t contentsLen) { - sFilename = filename; - - // Clear any previous error state - ClearError(); - - bool noerr = true; - mVensimLex.Initialize(contents, contentsLen); - int endtok = mVensimLex.GetEndToken(); - // now we call the bison built parser which will call back to VensimLex - // for the tokenizing - - int rval; - do { - rval = 0; - try { - mVensimLex.GetReady(); - rval = vpyyparse(); - - // Check if an error occurred during parsing - if (has_error_) { - log("%s\n", last_error_.c_str()); - log("Error at line %d position %d in file %s\n", mVensimLex.LineNumber(), mVensimLex.Position(), - sFilename.c_str()); - log(".... skipping the associated variable and looking for the next usable content.\n"); - pSymbolNameSpace->DeleteAllUnconfirmedAllocations(); - noerr = false; - ClearError(); // Clear for next iteration - if (!FindNextEq(false)) - break; - continue; - } - if (rval == '~') { // comment follows - if (!FindNextEq(true)) - break; - } else if (rval == '|') { - } else if (rval == VPTT_groupstar) { - // log("%s\n", mVensimLex.CurToken()->c_str()); - // only change this if a new number - ModelGroup *group_owner = NULL; - char c = mVensimLex.CurToken()->at(0); - if (_model->Groups().empty() || (_model->Groups().back()->sName[0] != c && c >= '0' && c <= '9')) { - std::string owner = *mVensimLex.CurToken(); - for (ModelGroup *g : _model->Groups()) { - if (g->sName == owner) { - group_owner = g; - break; - } - } - } - if (group_owner == NULL && !_model->Groups().empty()) - group_owner = _model->Groups().back(); - _model->Groups().push_back(new ModelGroup(*mVensimLex.CurToken(), group_owner)); - } else if (rval != endtok) { - log("Unknown terminal token %d\n", rval); - if (!FindNextEq(false)) - break; - } - - } catch (VensimParseSyntaxError &e) { - log("%s\n", e.str.c_str()); - log("Error at line %d position %d in file %s\n", mVensimLex.LineNumber(), mVensimLex.Position(), - sFilename.c_str()); - log(".... skipping the associated variable and looking for the next usable content.\n"); - pSymbolNameSpace->DeleteAllUnconfirmedAllocations(); - noerr = false; - if (!FindNextEq(false)) - break; - - } catch (...) { - // Log unknown exceptions to aid diagnosis and ensure visibility via C API - log("Unknown exception while parsing (Vensim) at line %d position %d in file %s\n", mVensimLex.LineNumber(), - mVensimLex.Position(), sFilename.c_str()); - log(".... skipping the associated variable and looking for the next usable content.\n"); - pSymbolNameSpace->DeleteAllUnconfirmedAllocations(); - noerr = false; - if (!FindNextEq(false)) - break; - } - } while (rval != endtok); - char buf[BUFLEN]; // plenty big for sketch info - if (rval == endtok) - this->mVensimLex.BufferReadLine(buf, BUFLEN); // get the marker line - while (true) { // read in the sketch information - if (strncmp(buf, "\\\\\\---///", 9) != 0) - break; - this->mVensimLex.ReadLine(buf, BUFLEN); // version line - if (strncmp(buf, "V300 ", 5) && strncmp(buf, "V364 ", 5)) { - log("Unrecognized version - can't read sketch info\n"); - noerr = false; - break; - } - VensimView *view = new VensimView; - - _model->AddView(view); - // next the title - this->mVensimLex.ReadLine(buf, BUFLEN); - view->SetTitle(buf + - 1); // skip the star - we can try to name modules with this eventually subject to name collisions - this->mVensimLex.ReadLine(buf, BUFLEN); // default font info - we can try to grab this later - int pos = 0; - char *tv = buf; - for (; pos < 8; pos++) { - tv = strchr(tv, '|'); - if (tv) - tv++; - else - break; - } - // the following does not really help - // if (tv) - //{ - // int ppix = 72; - // int ppiy = 72; - // sscanf(tv,"%d,%d",&ppix,&ppiy); - // _xratio = 72.0 / (double)ppix; - // _yratio = 72.0 / (double)ppiy; - //} - // else - { - _xratio = 1.0; - _yratio = 1.0; - } - view->ReadView(this, buf); // will return with buf populated at next view - } - // there may be options at the end - if (strncmp(buf, "///---\\\\\\", 9) == 0) { - while (this->mVensimLex.ReadLine(buf, BUFLEN)) // looking for settings maker - { - if (strncmp(buf, ":L\177<%^E!@", 9) == 0) { - while (this->mVensimLex.ReadLine(buf, BUFLEN)) { - int type; - char *curpos = GetIntChar(buf, type, ':'); - if (type == 15) // fourth entry is integration type - { - int im; - for (int i = 0; i < 4; i++) - curpos = GetInt(curpos, im); - Integration_Type it = Integration_Type_EULER; - switch (im) { - case 0: - case 2: - default: - it = Integration_Type_EULER; - break; - case 1: - case 5: - it = Integration_Type_RK4; - break; - case 3: - case 4: - it = Integration_Type_RK2; - break; - } - _model->SetIntegrationType(it); - } else if (type == 22) // units equialences - { - _model->UnitEquivs().push_back(curpos); - } - } - break; - } - } - } - _model->SetMacroFunctions(mMacroFunctions); - - if (bLongName) { - // try to replace variable names with long names from the documentaion - std::vector vars = _model->GetVariables(NULL); // all symbols that are variables - for (Variable *var : vars) { - std::string alt = compress_whitespace(var->Comment()); - if (alt == "Backlog") { - bLongName = true; - } - if (!alt.empty() && alt.size() < 80 && pSymbolNameSpace->Rename(var, alt)) { - var->SetAlternateName(alt); - } - } - } - // if there was an error, don't attempt to write out the file - return noerr; -} - -char *VensimParse::GetIntChar(char *s, int &val, char c) { - char *tv; - for (tv = s; *tv; tv++) { - if (*tv == c) { - *tv++ = '\0'; - break; - } - } - val = atoi(s); - return tv; -} -char *VensimParse::GetInt(char *s, int &val) { - char *tv; - for (tv = s; *tv; tv++) { - if (*tv == ',') { - *tv++ = '\0'; - break; - } - } - val = atoi(s); - return tv; -} -char *VensimParse::GetString(char *s, std::string &name) { - char *tv = s; - if (*tv == '\"') { - for (tv++; *tv; tv++) { - if (*tv == '\"') { - tv++; - assert(*tv == ','); - *tv++ = '\0'; - break; - } else if (*tv == '\\' && tv[1] == '\"') - tv++; - } - } else { - for (tv = s; *tv; tv++) { - if (*tv == ',') { - *tv++ = '\0'; - break; - } - } - } - name = s; - return tv; -} - -Variable *VensimParse::FindVariable(const std::string &name) { - Variable *var = static_cast(pSymbolNameSpace->Find(name)); - if (var && var->isType() == Symtype_Variable) - return var; - return NULL; -} - -Variable *VensimParse::InsertVariable(const std::string &name) { - Variable *var = static_cast(pSymbolNameSpace->Find(name)); - if (var && var->isType() != Symtype_Variable && var->isType() != Symtype_Function) { - has_error_ = true; - last_error_ = "Type meaning mismatch for " + name; - return nullptr; - } - if (!var) { - var = new Variable(pSymbolNameSpace, name); - // this will insert it into the name space for hash lookup as well - } - return var; -} -Units *VensimParse::InsertUnits(const std::string &name) { - std::string uname = ">" + name; // an illegal variable name since we allow the same names to be used for vars and - // units - could use a separate namespace - Units *u = static_cast(pSymbolNameSpace->Find(uname)); - if (u && u->isType() != Symtype_Units) { - has_error_ = true; - last_error_ = "Type meaning mismatch for " + name; - return nullptr; - } - if (!u) { - u = new Units(pSymbolNameSpace, uname); - } - return u; -} - -UnitExpression *VensimParse::InsertUnitExpression(Units *u) { - UnitExpression *uni = new UnitExpression(pSymbolNameSpace, u); - return uni; -} - -// find the beginning of the next equation - for error recovery -bool VensimParse::FindNextEq(bool want_comment) { - if (want_comment && this->pActiveVar) { - std::string comment = mVensimLex.GetComment("|"); - if (!comment.empty()) // multile appearances okay - take last non empty - this->pActiveVar->SetComment(comment); - if (mVensimLex.LastCommentHadSupplementary()) - this->pActiveVar->SetSupplementary(true); - } - // just zip through to the first | then whatever follows is it - return mVensimLex.FindToken("|"); -} - -LeftHandSide *VensimParse::AddExceptInterp(ExpressionVariable *var, SymbolListList *except, int interpmode) { - return new LeftHandSide(pSymbolNameSpace, var, NULL, except, interpmode); -} -SymbolList *VensimParse::SymList(SymbolList *in, Variable *add, bool bang, Variable *end) { - SymbolList *sl; - if (in) - sl = in->Append(add, bang); - else - sl = new SymbolList(pSymbolNameSpace, add, bang); - if (end) { /* actually shortcut for (axx-ayy) */ - int i, j; - Variable *v; - std::string start = add->GetName(); - std::string finish = end->GetName(); - for (i = start.length(); --i > 0;) { - if (start[i - 1] < '0' || start[i - 1] > '9') - break; - } - for (j = finish.length(); --j > 0;) { - if (finish[j - 1] < '0' || finish[j - 1] > '9') - break; - } - int low = atoi(start.c_str() + i); - int high = atoi(finish.c_str() + j); - if (i != j || start.compare(0, j, finish, 0, j) || low >= high) { - has_error_ = true; - last_error_ = "Bad subscript range specification"; - delete sl; - return nullptr; - } - start.erase(i, std::string::npos); - for (i = low + 1; i < high; i++) { - finish = start + std::to_string(i); - v = static_cast(pSymbolNameSpace->Find(finish)); - if (!v) - v = new Variable(pSymbolNameSpace, finish); - sl->Append(v, bang); - } - sl->Append(end, bang); - } - return sl; -} - -SymbolList *VensimParse::MapSymList(SymbolList *in, Variable *range, SymbolList *list) { - list->SetMapRange(range); - if (in) { - in->Append(list); - return in; - } - return list; -} -UnitExpression *VensimParse::UnitsDiv(UnitExpression *num, UnitExpression *denom) { - return num->Divide(denom); -} -UnitExpression *VensimParse::UnitsMult(UnitExpression *f, UnitExpression *s) { - return f->Multiply(s); -} -UnitExpression *VensimParse::UnitsRange(UnitExpression *e, double minval, double maxval, double increment) { - if (e == NULL) { - Units *units = VPObject->InsertUnits("1"); - if (units) { - e = VPObject->InsertUnitExpression(units); - } - } - e->SetRange(minval, maxval, increment); - return e; -} - -SymbolListList *VensimParse::ChainSublist(SymbolListList *sll, SymbolList *nsl) { - if (!sll) - sll = new SymbolListList(pSymbolNameSpace, nsl); - else - sll->Append(nsl); - return sll; -} -ExpressionList *VensimParse::ChainExpressionList(ExpressionList *el, Expression *e) { - if (!el) - el = new ExpressionList(pSymbolNameSpace); - return el->Append(e); -} -Expression *VensimParse::NumExpression(double num) { - return new ExpressionNumber(pSymbolNameSpace, num); -} -Expression *VensimParse::LiteralExpression(const char *lit) { - return new ExpressionLiteral(pSymbolNameSpace, lit); -} -ExpressionVariable *VensimParse::VarExpression(Variable *var, SymbolList *subs) { - return new ExpressionVariable(pSymbolNameSpace, var, subs); -} -ExpressionSymbolList *VensimParse::SymlistExpression(SymbolList *subs, SymbolList *map) { - return new ExpressionSymbolList(pSymbolNameSpace, subs, map); -} -Expression *VensimParse::OperatorExpression(int oper, Expression *exp1, Expression *exp2) { - switch (oper) { - case '*': - return new ExpressionMultiply(pSymbolNameSpace, exp1, exp2); - case '/': - return new ExpressionDivide(pSymbolNameSpace, exp1, exp2); - case '+': - if (!exp1 && exp2 && exp2->GetType() == EXPTYPE_Number) - return exp2; /* unary plus just ignore */ - return new ExpressionAdd(pSymbolNameSpace, exp1, exp2); - case '-': - if (!exp1) { - if (exp2 && exp2->GetType() == EXPTYPE_Number) { - exp2->FlipSign(); - return exp2; - } - return new ExpressionUnaryMinus(pSymbolNameSpace, exp2, NULL); - } - return new ExpressionSubtract(pSymbolNameSpace, exp1, exp2); - case '^': - return new ExpressionPower(pSymbolNameSpace, exp1, exp2); - case '(': - assert(!exp2); - return new ExpressionParen(pSymbolNameSpace, exp1, NULL); - case '<': - case '>': - case VPTT_le: - case VPTT_ge: - case VPTT_ne: - case VPTT_and: - case VPTT_or: - case '=': - return new ExpressionLogical(pSymbolNameSpace, exp1, exp2, oper); - case VPTT_not: - assert(exp2 == NULL); - return new ExpressionLogical(pSymbolNameSpace, NULL, exp1, oper); - default: - has_error_ = true; - last_error_ = "Unknown operator internal error "; - return nullptr; - } -} -Expression *VensimParse::FunctionExpression(Function *func, ExpressionList *eargs) { - if (func->NumberArgs() >= 0 && - ((!eargs && func->NumberArgs() > 0) || (eargs && func->NumberArgs() != eargs->Length()))) { - has_error_ = true; - last_error_ = "Argument count mismatch for "; - last_error_.append(func->GetName()); - return nullptr; - } - if (func->IsMemoryless() && !func->IsTimeDependent()) - return new ExpressionFunction(pSymbolNameSpace, func, eargs); - return new ExpressionFunctionMemory(pSymbolNameSpace, func, eargs); -} -Expression *VensimParse::LookupExpression(ExpressionVariable *var, ExpressionList *args) { - if (args->Length() == 1) - return new ExpressionLookup(pSymbolNameSpace, var, args->GetExp(0)); - // really an error so we use uknown function - const std::string &name = var->GetVariable()->GetName(); - Function *f = new UnknownFunction(new SymbolNameSpace(), name, args->Length()); - return new ExpressionFunction(pSymbolNameSpace, f, args); -} - -ExpressionTable *VensimParse::TablePairs(ExpressionTable *table, double x, double y) { - if (!table) - table = new ExpressionTable(pSymbolNameSpace); - table->AddPair(x, y); - return table; -} - -ExpressionTable *VensimParse::XYTableVec(ExpressionTable *table, double val) { - if (!table) - table = new ExpressionTable(pSymbolNameSpace); - table->AddPair(val, 0); // fix these after reducing - return table; -} - -ExpressionTable *VensimParse::TableRange(ExpressionTable *table, double x1, double y1, double x2, double y2) { - table->AddRange(x1, y1, x2, y2); - return table; -} - -void VensimParse::MacroStart() { - mInMacro = true; - pMainSymbolNameSpace = pSymbolNameSpace; - pSymbolNameSpace = new SymbolNameSpace(); // local name space for macro variables and macro name - macro name will go - // into the main name space on close - ReadyFunctions(); // against this new name space - somewhat duplicative -} - -void VensimParse::MacroExpression(Variable *name, ExpressionList *margs) { - // the macro functiongoes against the main name space - everything else is local - mMacroFunctions.push_back(new MacroFunction(pMainSymbolNameSpace, pSymbolNameSpace, name->GetName(), margs)); -} -void VensimParse::MacroEnd() { - pSymbolNameSpace = pMainSymbolNameSpace; - mInMacro = false; -} - -bool VensimParse::LetterPolarity() const { - return _model->LetterPolarity(); -} -void VensimParse::SetLetterPolarity(bool set) { - _model->SetLetterPolarity(set); -} diff --git a/src/xmutil/third_party/xmutil/Vensim/VensimParse.h b/src/xmutil/third_party/xmutil/Vensim/VensimParse.h deleted file mode 100644 index ce42796a3..000000000 --- a/src/xmutil/third_party/xmutil/Vensim/VensimParse.h +++ /dev/null @@ -1,120 +0,0 @@ -#ifndef _XMUTIL_VENSIM_VENSIMPARSE_H -#define _XMUTIL_VENSIM_VENSIMPARSE_H -#include - -#include "../Function/Function.h" -#include "../Symbol/Equation.h" -#include "../Symbol/Parse.h" -#include "../Symbol/Symbol.h" -#include "../Symbol/Units.h" -#include "VensimLex.h" - -#define BUFLEN 4096 // for reading sketch info - -class VensimView; - -class VensimParseSyntaxError { -public: - std::string str; -}; - -class VensimParse { -public: - VensimParse(Model *model); - ~VensimParse(void); - void ReadyFunctions(); - bool ProcessFile(const std::string &filename, const char *contents, size_t contentsLen); - inline int yylex(void) { - return mVensimLex.yylex(); - } - int yyerror(const char *str); - Equation *AddEq(LeftHandSide *lhs, Expression *ex, ExpressionList *exl, int tok); - Equation *AddTable(LeftHandSide *lhs, Expression *ex, ExpressionTable *table, bool legacy); - inline SymbolNameSpace *GetSymbolNameSpace(void) { - return pSymbolNameSpace; - } - Variable *InsertVariable(const std::string &name); - Variable *FindVariable(const std::string &name); - Units *InsertUnits(const std::string &name); - UnitExpression *InsertUnitExpression(Units *u); - void AddFullEq(Equation *eq, UnitExpression *un); - LeftHandSide *AddExceptInterp(ExpressionVariable *var, SymbolListList *except, int interpmode); - SymbolList *SymList(SymbolList *in, Variable *add, bool bang, Variable *end); - SymbolList *MapSymList(SymbolList *in, Variable *range, SymbolList *list); - UnitExpression *UnitsDiv(UnitExpression *num, UnitExpression *denom); - UnitExpression *UnitsMult(UnitExpression *f, UnitExpression *s); - UnitExpression *UnitsRange(UnitExpression *e, double minval, double maxval, double increment); - SymbolListList *ChainSublist(SymbolListList *sll, SymbolList *nsl); - ExpressionList *ChainExpressionList(ExpressionList *el, Expression *e); - Expression *NumExpression(double num); - Expression *LiteralExpression(const char *lit); - ExpressionVariable *VarExpression(Variable *var, SymbolList *subs); - ExpressionSymbolList *SymlistExpression(SymbolList *sym, SymbolList *map); - Expression *OperatorExpression(int oper, Expression *exp1, Expression *exp2); - Expression *FunctionExpression(Function *func, ExpressionList *eargs); - Expression *LookupExpression(ExpressionVariable *var, ExpressionList *args); - ExpressionTable *TablePairs(ExpressionTable *table, double x, double y); - ExpressionTable *XYTableVec(ExpressionTable *table, double val); - ExpressionTable *TableRange(ExpressionTable *table, double x1, double y1, double x2, double y2); - void MacroStart(); - void MacroExpression(Variable *macro, ExpressionList *margs); - void MacroEnd(); - - double Xratio() const { - return _xratio; - } - double Yratio() const { - return _yratio; - } - VensimLex &Lexer() { - return mVensimLex; - } - char *GetInt(char *buf, int &val); - char *GetIntChar(char *buf, int &val, char c); - char *GetString(char *buf, std::string &s); - - void SetLongName(bool set) { - bLongName = set; - } - bool LongName() const { - return bLongName; - } - - bool LetterPolarity() const; - void SetLetterPolarity(bool set); - - // Error state management - for C/Rust compatibility - bool HasError() const { - return has_error_; - } - const std::string &GetLastError() const { - return last_error_; - } - void ClearError() { - has_error_ = false; - last_error_.clear(); - } - -private: - // Error state management members - bool has_error_ = false; - std::string last_error_; - - bool FindNextEq(bool want_comment); - Model *_model; - std::string sFilename; - VensimLex mVensimLex; - VensimParseSyntaxError mSyntaxError; - SymbolNameSpace *pSymbolNameSpace; - SymbolNameSpace *pMainSymbolNameSpace; - Variable *pActiveVar; - double _xratio; - double _yratio; - bool mInMacro; - bool bLongName; - std::vector mMacroFunctions; -}; - -extern VensimParse *VPObject; - -#endif \ No newline at end of file diff --git a/src/xmutil/third_party/xmutil/Vensim/VensimParseFunctions.cpp b/src/xmutil/third_party/xmutil/Vensim/VensimParseFunctions.cpp deleted file mode 100644 index ba3208645..000000000 --- a/src/xmutil/third_party/xmutil/Vensim/VensimParseFunctions.cpp +++ /dev/null @@ -1,86 +0,0 @@ -#include "VensimParseFunctions.h" - -#include "../Symbol/Parse.h" -#include "../XMUtil.h" -#include "VensimParse.h" - -void vpyy_addfulleq(Equation *eq, UnitExpression *un) { - return VPObject->AddFullEq(eq, un); -} -Equation *vpyy_addeq(LeftHandSide *lhs, Expression *ex, ExpressionList *exl, int token) { - return VPObject->AddEq(lhs, ex, exl, token); -} -Equation *vpyy_add_lookup(LeftHandSide *lhs, Expression *ex, ExpressionTable *tvl, int legacy) { - return VPObject->AddTable(lhs, ex, tvl, legacy != 0); -} -LeftHandSide *vpyy_addexceptinterp(ExpressionVariable *var, SymbolListList *except, int interpmode) { - return VPObject->AddExceptInterp(var, except, interpmode); -} -SymbolList *vpyy_symlist(SymbolList *in, Variable *add, int bang, Variable *end) { - return VPObject->SymList(in, add, !!bang, end); -} -SymbolList *vpyy_mapsymlist(SymbolList *in, Variable *maprange, SymbolList *list) { - return VPObject->MapSymList(in, maprange, list); -} -UnitExpression *vpyy_unitsdiv(UnitExpression *num, UnitExpression *denom) { - return VPObject->UnitsDiv(num, denom); -} -UnitExpression *vpyy_unitsmult(UnitExpression *f, UnitExpression *s) { - return VPObject->UnitsMult(f, s); -} -UnitExpression *vpyy_unitsrange(UnitExpression *f, double minval, double maxval, double increment) { - return VPObject->UnitsRange(f, minval, maxval, increment); -} -SymbolListList *vpyy_chain_sublist(SymbolListList *sll, SymbolList *nsl) { - return VPObject->ChainSublist(sll, nsl); -} -ExpressionList *vpyy_chain_exprlist(ExpressionList *el, Expression *e) { - return VPObject->ChainExpressionList(el, e); -} -Expression *vpyy_num_expression(double num) { - return VPObject->NumExpression(num); -} -Expression *vpyy_literal_expression(const char *lit) { - return VPObject->LiteralExpression(lit); -} -ExpressionVariable *vpyy_var_expression(Variable *var, SymbolList *subs) { - return VPObject->VarExpression(var, subs); -} -ExpressionSymbolList *vpyy_symlist_expression(SymbolList *sym, SymbolList *map) { - return VPObject->SymlistExpression(sym, map); -} -Expression *vpyy_operator_expression(int oper, Expression *exp1, Expression *exp2) { - return VPObject->OperatorExpression(oper, exp1, exp2); -} -Expression *vpyy_function_expression(Function *func, ExpressionList *eargs) { - return VPObject->FunctionExpression(func, eargs); -} -Expression *vpyy_lookup_expression(ExpressionVariable *var, ExpressionList *args) { - return VPObject->LookupExpression(var, args); -} -ExpressionTable *vpyy_tablepair(ExpressionTable *table, double x, double y) { - return VPObject->TablePairs(table, x, y); -} -ExpressionTable *vpyy_tablevec(ExpressionTable *table, double val) { - return VPObject->XYTableVec(table, val); -} -ExpressionTable *vpyy_tablerange(ExpressionTable *table, double x1, double y1, double x2, double y2) { - return VPObject->TableRange(table, x1, y1, x2, y2); -} -void vpyy_macro_start() { - VPObject->MacroStart(); -} -void vpyy_macro_expression(Variable *name, ExpressionList *margs) { - VPObject->MacroExpression(name, margs); -} -void vpyy_macro_end() { - VPObject->MacroEnd(); -} - -/* the default functions called by parser */ -int vpyylex(void) { - return VPObject->yylex(); -} -void vpyyerror(const char *str) { - VPObject->yyerror(str); -} diff --git a/src/xmutil/third_party/xmutil/Vensim/VensimParseFunctions.h b/src/xmutil/third_party/xmutil/Vensim/VensimParseFunctions.h deleted file mode 100644 index e113957a3..000000000 --- a/src/xmutil/third_party/xmutil/Vensim/VensimParseFunctions.h +++ /dev/null @@ -1,30 +0,0 @@ -#ifndef _XMUTIL_VENSIM_VENSIMPARSEFUNCTIONS_H -#define _XMUTIL_VENSIM_VENSIMPARSEFUNCTIONS_H -#include "../Symbol/Parse.h" - -void vpyy_addfulleq(Equation *eq, UnitExpression *un); -Equation *vpyy_addeq(LeftHandSide *lhs, Expression *ex, ExpressionList *exl, int token); -Equation *vpyy_add_lookup(LeftHandSide *lhs, Expression *ex, ExpressionTable *tvl, int legacy); -LeftHandSide *vpyy_addexceptinterp(ExpressionVariable *var, SymbolListList *except, int interpmode); -SymbolList *vpyy_symlist(SymbolList *in, Variable *add, int bang, Variable *end); -SymbolList *vpyy_mapsymlist(SymbolList *in, Variable *maprange, SymbolList *list); -UnitExpression *vpyy_unitsdiv(UnitExpression *num, UnitExpression *denom); -UnitExpression *vpyy_unitsmult(UnitExpression *f, UnitExpression *s); -UnitExpression *vpyy_unitsrange(UnitExpression *f, double minval, double maxval, double increment); -SymbolListList *vpyy_chain_sublist(SymbolListList *sll, SymbolList *nsl); -ExpressionList *vpyy_chain_exprlist(ExpressionList *el, Expression *e); -Expression *vpyy_num_expression(double num); -Expression *vpyy_literal_expression(const char *tok); -ExpressionVariable *vpyy_var_expression(Variable *var, SymbolList *subs); -ExpressionSymbolList *vpyy_symlist_expression(SymbolList *subs, SymbolList *map); -Expression *vpyy_operator_expression(int oper, Expression *exp1, Expression *exp2); -Expression *vpyy_function_expression(Function *func, ExpressionList *args); -Expression *vpyy_lookup_expression(ExpressionVariable *var, ExpressionList *args); -ExpressionTable *vpyy_tablepair(ExpressionTable *table, double x, double y); -ExpressionTable *vpyy_tablevec(ExpressionTable *table, double val); -ExpressionTable *vpyy_tablerange(ExpressionTable *table, double x1, double y1, double x2, double y2); -void vpyy_macro_start(); -void vpyy_macro_expression(Variable *name, ExpressionList *margs); -void vpyy_macro_end(); - -#endif diff --git a/src/xmutil/third_party/xmutil/Vensim/VensimView.cpp b/src/xmutil/third_party/xmutil/Vensim/VensimView.cpp deleted file mode 100644 index a82398378..000000000 --- a/src/xmutil/third_party/xmutil/Vensim/VensimView.cpp +++ /dev/null @@ -1,451 +0,0 @@ -#include "VensimView.h" - -#include "../Symbol/Variable.h" -#include "VensimParse.h" - -VensimVariableElement::VensimVariableElement(VensimView *view, char *curpos, char *buf, VensimParse *parser) { - std::string name; - curpos = parser->GetString(curpos, name); // this might be an index number - - curpos = parser->GetInt(curpos, _x); - curpos = parser->GetInt(curpos, _y); - curpos = parser->GetInt(curpos, _width); - curpos = parser->GetInt(curpos, _height); - - int shape, bits; - curpos = parser->GetInt(curpos, shape); - if (shape & (1 << 5)) - _attached = true; - else - _attached = false; - curpos = parser->GetInt(curpos, bits); - if ((bits & 1)) - _ghost = false; - else - _ghost = true; - _cross_level = false; - -#ifndef NDEBUG - if (name == "P100") - curpos = curpos; -#endif - - // try to find the variable - _variable = parser->FindVariable(name); - if (_variable) { - if (_variable->GetView()) - _ghost = true; // only allow 1 definition - else if (!_ghost) { - _variable->SetView(view); - if (_attached) - _variable->MarkAsFlow(); - } - } else { - std::string *nname = SymbolNameSpace::ToLowerSpace(name); - if (*nname != "time") // any others? - log("Can't find - %s\n", name.c_str()); - delete nname; - } -} -VensimVariableElement::VensimVariableElement(VensimView *view, Variable *var, int x, int y) { - _x = x; - _y = y; - _width = _height = 0; - _ghost = var->GetView() != NULL; - _cross_level = false; - _variable = var; - _variable->SetView(view); -#ifndef NDEBUG - if (var->GetName() == "P100") - x = x; -#endif -} - -VensimCommentElement::VensimCommentElement(char *curpos, char *buf, VensimParse *parser) { - std::string name; - curpos = parser->GetString(curpos, name); // this might be an index number - - curpos = parser->GetInt(curpos, _x); - curpos = parser->GetInt(curpos, _y); - curpos = parser->GetInt(curpos, _width); - curpos = parser->GetInt(curpos, _height); - - int shape, bits; - curpos = parser->GetInt(curpos, shape); - curpos = parser->GetInt(curpos, bits); - - if (bits & (1 << 2)) // scratch name - it is the next line - { - parser->Lexer().ReadLine(buf, BUFLEN); - name = buf; - } -} - -VensimValveElement::VensimValveElement(char *curpos, char *buf, VensimParse *parser) { - std::string name; - curpos = parser->GetString(curpos, name); // this might be an index number - - curpos = parser->GetInt(curpos, _x); - curpos = parser->GetInt(curpos, _y); - curpos = parser->GetInt(curpos, _width); - curpos = parser->GetInt(curpos, _height); - int shape; - curpos = parser->GetInt(curpos, shape); - if (shape & (1 << 5)) - _attached = true; - else - _attached = false; -} - -bool VensimVariableElement::Ghost(std::set *adds) { - if (_ghost && !_cross_level) { - if (adds) { - std::set::iterator it = adds->find(this->GetVariable()); - if (it != adds->end()) { - adds->erase(it); - _cross_level = true; // so it will continue to return false - return false; - } - } - return true; - } - return false; -} - -VensimConnectorElement::VensimConnectorElement(char *curpos, char *buf, VensimParse *parser) { - curpos = parser->GetInt(curpos, _from); - curpos = parser->GetInt(curpos, _to); - std::string ignore; - curpos = parser->GetString(curpos, ignore); - curpos = parser->GetString(curpos, ignore); - int polarity_ascii; - curpos = parser->GetInt(curpos, polarity_ascii); - if (polarity_ascii == 'S' || polarity_ascii == 's') { - parser->SetLetterPolarity(true); - _polarity = '+'; - } else if (polarity_ascii == 'O' || polarity_ascii == '0') { - parser->SetLetterPolarity(true); - _polarity = '-'; - } else - _polarity = polarity_ascii; // might be invalid - curpos = parser->GetString(curpos, ignore); - curpos = parser->GetString(curpos, ignore); - curpos = parser->GetString(curpos, ignore); - curpos = parser->GetString(curpos, ignore); - curpos = parser->GetString(curpos, ignore); - curpos = parser->GetString(curpos, ignore); - - int npoints; - sscanf(curpos, "%d|(%d,%d)", &npoints, &_x, &_y); - _npoints = 1; // todo get all of them - - std::string name; - curpos = parser->GetString(curpos, name); // this might be an index number -} - -VensimConnectorElement::VensimConnectorElement(int from, int to, int x, int y) { - _from = from; - _to = to; - _npoints = 1; - _x = x; - _y = y; -} - -bool VensimConnectorElement::ScalePoints(double xs, double ys, int xo, int yo) { - if (_x != 0 || _y != 0) // invalide leave it alone - { - _x = _x * xs + xo; - _y = _y * ys + yo; - } - return true; -} - -void VensimView::ReadView(VensimParse *parser, char *buf) { - VensimLex &lexer = parser->Lexer(); - while (true) { - lexer.ReadLine(buf, BUFLEN); // version line - if (buf[0] < '0' || buf[0] > '9') - break; - int len = 0; - int type = -1; - int uid = -1; - char *curpos = parser->GetInt(buf, type); - curpos = parser->GetInt(curpos, uid); - if (type >= 0 && uid >= 0) // otherwise ignore - { - if (uid > len) { - len = uid + 25; - vElements.resize(len + 1, NULL); - } - assert(vElements[uid] == NULL); - switch (type) { - case 10: // a variable - vElements[uid] = new VensimVariableElement(this, curpos, buf, parser); - break; - case 11: // a valve if connected to a variable always just after it in the lest (??) - vElements[uid] = new VensimValveElement(curpos, buf, parser); - break; - case 12: // a comment including clouds - vElements[uid] = new VensimCommentElement(curpos, buf, parser); - break; - case 1: // a connector - vElements[uid] = new VensimConnectorElement(curpos, buf, parser); - break; - case 30: // a ?????? - break; - default: - assert(false); - break; - } - } - } -} - -int VensimView::GetNextUID() { - for (size_t i = vElements.size(); --i > 0;) { - if (!vElements[i]) - return i; - } - vElements.resize(vElements.size() + 25, NULL); - return GetNextUID(); -} - -int VensimView::SetViewStart(int startx, int starty, double xratio, double yratio, int uid_start) { - _uid_offset = uid_start; - if (this->vElements.empty()) - return _uid_offset; - int min_x = INT32_MAX; - int min_y = INT32_MAX; - for (VensimViewElement *ele : vElements) { - if (ele) { - if (ele->X() < min_x) - min_x = ele->X(); - if (ele->Y() < min_y) - min_y = ele->Y(); - } - } - int off_x = std::round(startx - min_x * xratio); - int off_y = std::round(starty - min_y * yratio); - for (VensimViewElement *ele : vElements) { - if (ele) { - if (!ele->ScalePoints(xratio, yratio, off_x, off_y)) { - ele->SetX(std::round(ele->X() * xratio + off_x)); - ele->SetY(std::round(ele->Y() * yratio + off_y)); - ele->SetWidth(std::round(ele->Width() * xratio)); - ele->SetHeight(std::round(ele->Height() * yratio)); - } - } - } - return _uid_offset + vElements.size(); -} - -int VensimView::GetViewMaxX(int defval) { - if (this->vElements.empty()) - return defval; - int max_x = -INT32_MAX; - for (VensimViewElement *ele : vElements) { - if (ele) { - if (ele->X() > max_x) - max_x = ele->X(); - } - } - return max_x; -} -int VensimView::GetViewMaxY(int defval) { - if (this->vElements.empty()) - return defval; - int max_y = -INT32_MAX; - for (VensimViewElement *ele : vElements) { - if (ele) { - if (ele->Y() > max_y) - max_y = ele->Y(); - } - } - return max_y; -} - -bool VensimView::UpgradeGhost(Variable *var) { - for (VensimViewElement *ele : vElements) { - if (ele && ele->Type() == VensimViewElement::ElementTypeVARIABLE) { - VensimVariableElement *vele = static_cast(ele); - if (vele->GetVariable() == var) { - assert(vele->Ghost(NULL)); - vele->SetGhost(false); - var->SetView(this); // now done - return true; - } - } - } - return false; -} - -bool VensimView::empty() const { - int i = 5; - if (this->sTitle == "Energy sensitivity") - i++; - for (VensimViewElement *vve : vElements) { - if (vve) { - Variable *var = vve->GetVariable(); - if (var && var->GetView() == this && !var->Unwanted()) - return false; - } - } - return true; -} - -bool VensimView::AddFlowDefinition(Variable *var, Variable *upstream, Variable *downstream) { - // for flows we are looking for any stocks that use the flow - // for - int xstart, ystart, xend, yend; - xstart = ystart = xend = yend = 0; - bool startfound = false; - bool endfound = false; - for (VensimViewElement *ele : vElements) { - if (ele && ele->Type() == VensimViewElement::ElementTypeVARIABLE) { - VensimVariableElement *vele = static_cast(ele); - if (vele->GetVariable() == upstream) { - xstart = vele->X(); - ystart = vele->Y(); - startfound = true; - if (endfound) - break; - } else if (vele->GetVariable() == downstream) { - xend = vele->X(); - yend = vele->Y(); - endfound = true; - if (startfound) - break; - } - } - } - if (!startfound && !endfound) - return false; // can't find anything - should not happen - if (startfound && endfound) // put in the middle - { - xstart = (xstart + xend) / 2; - ystart = (ystart + yend) / 2; - } else if (startfound) { - xstart += 60; - } else if (endfound) { - xstart = xend - 60; - ystart = yend; - } - // add the var to this view - int uid = this->GetNextUID(); - vElements[uid] = new VensimVariableElement(this, var, xstart, ystart); - return true; -} - -bool VensimView::AddVarDefinition(Variable *var, int x, int y) { - // add the var to this view - int uid = this->GetNextUID(); - vElements[uid] = new VensimVariableElement(this, var, x, y); - return true; -} - -// add if msising - if extra just ignore -void VensimView::CheckGhostOwners() { - int uid; - int n = vElements.size(); - for (uid = 0; uid < n; uid++) { - VensimViewElement *ele = vElements[uid]; - if (ele && ele->Type() == VensimViewElement::ElementTypeVARIABLE) { - VensimVariableElement *vele = static_cast(ele); - Variable *var = vele->GetVariable(); - if (var && var->GetView() == NULL) { - var->SetView(this); - vele->SetGhost(false); - } - } - } -} - -// add if msising - if extra just ignore -void VensimView::CheckLinksIn() { - int uid; - int n = vElements.size(); - for (uid = 0; uid < n; uid++) { - VensimViewElement *ele = vElements[uid]; - if (ele && ele->Type() == VensimViewElement::ElementTypeVARIABLE) { - VensimVariableElement *vele = static_cast(ele); - Variable *var = vele->GetVariable(); - if (var && var->VariableType() != XMILE_Type_STOCK && !vele->Ghost(NULL)) { - std::vector ins = var->GetInputVars(); - for (Variable *in : ins) { - if (!this->FindInArrow(in, uid) && in->VariableType() != XMILE_Type_ARRAY && - in->VariableType() != XMILE_Type_ARRAY_ELM && in->VariableType() != XMILE_Type_UNKNOWN) { - int fromuid = this->FindVariable(in, vele->X(), vele->Y() + 30); - int x = (vElements[fromuid]->X() + vele->X()) / 2; - int y = (vElements[fromuid]->Y() + vele->Y()) / 2; - int nuid = this->GetNextUID(); - vElements[nuid] = new VensimConnectorElement(fromuid, uid, x, y); - } - } - this->RemoveExtraArrowsIn(ins, uid); // sometimes there are anomolous arrows that show up - } - } - } -} - -bool VensimView::FindInArrow(Variable *in, int target) { - for (VensimViewElement *ele : this->vElements) { - if (ele && ele->Type() == VensimViewElement::ElementTypeCONNECTOR) { - VensimConnectorElement *cele = static_cast(ele); - int to = cele->To(); - VensimValveElement *tele = static_cast(vElements[to]); - if (tele && tele->Type() == VensimViewElement::ElementTypeVALVE && tele->Attached()) - to++; - if (to == target) { - VensimVariableElement *from = static_cast(vElements[cele->From()]); - if (from && from->Type() == VensimViewElement::ElementTypeVALVE && tele->Attached() && - static_cast(vElements[cele->From()])->Attached()) - from = static_cast(vElements[cele->From() + 1]); - if (from->Type() == VensimViewElement::ElementTypeVARIABLE && from->GetVariable() == in) - return true; - } - } - } - return false; -} - -void VensimView::RemoveExtraArrowsIn(std::vector ins, int target) { - for (VensimViewElement *ele : this->vElements) { - if (ele && ele->Type() == VensimViewElement::ElementTypeCONNECTOR) { - VensimConnectorElement *cele = static_cast(ele); - int to = cele->To(); - VensimValveElement *tele = static_cast(vElements[to]); - if (tele && tele->Type() == VensimViewElement::ElementTypeVALVE && tele->Attached()) - to++; - if (to == target) { - VensimVariableElement *from = static_cast(vElements[cele->From()]); - if (from && from->Type() == VensimViewElement::ElementTypeVALVE && tele->Attached() && - static_cast(vElements[cele->From()])->Attached()) - from = static_cast(vElements[cele->From() + 1]); - bool found = false; - for (Variable *in : ins) { - if (from->GetVariable() == in) { - found = true; - break; - } - } - if (!found) - cele->Invalidate(); - } - } - } -} - -int VensimView::FindVariable(Variable *in, int x, int y) { - int uid = 0; - for (VensimViewElement *ele : this->vElements) { - if (ele && ele->Type() == VensimViewElement::ElementTypeVARIABLE) { - VensimVariableElement *vele = static_cast(ele); - if (vele->GetVariable() == in) - return uid; - } - uid++; - } - uid = GetNextUID(); - vElements[uid] = new VensimVariableElement(this, in, x, y); - return uid; -} diff --git a/src/xmutil/third_party/xmutil/Vensim/VensimView.h b/src/xmutil/third_party/xmutil/Vensim/VensimView.h deleted file mode 100644 index f6392d3c8..000000000 --- a/src/xmutil/third_party/xmutil/Vensim/VensimView.h +++ /dev/null @@ -1,177 +0,0 @@ -#ifndef _XMUTIL_VENSIM_VENSIMVIEW_H -#define _XMUTIL_VENSIM_VENSIMVIEW_H -#include - -#include "../Model.h" -#include "../Symbol/Parse.h" -#include "../Symbol/Symbol.h" -#include "VensimLex.h" -class VensimParse; -class VensimView; -class Variable; - -class VensimViewElement { -public: - enum ElementType { ElementTypeVARIABLE, ElementTypeVALVE, ElementTypeCOMMENT, ElementTypeCONNECTOR }; - virtual ~VensimViewElement() = default; // Virtual destructor for proper polymorphic deletion - virtual ElementType Type() = 0; - virtual bool ScalePoints(double xratio, double yratio, int offx, int offy) { - return false; - } - virtual Variable *GetVariable() const { - return NULL; - } - int X() { - return _x; - } - void SetX(int x) { - _x = x; - } - int Y() { - return _y; - } - void SetY(int y) { - _y = y; - } - int Width() const { - return _width; - } - void SetWidth(int w) { - _width = w; - } - int Height() const { - return _height; - } - void SetHeight(int h) { - _height = h; - } - -protected: - int _x; - int _y; - int _width; - int _height; -}; -typedef std::vector VensimViewElements; -class VensimVariableElement : public VensimViewElement { -public: - VensimVariableElement(VensimView *view, char *curpos, char *buf, VensimParse *parser); - VensimVariableElement(VensimView *view, Variable *var, int x, int y); - ElementType Type() override { - return ElementTypeVARIABLE; - } - virtual Variable *GetVariable() const override { - return _variable; - } - bool Ghost(std::set *adds); - bool CrossLevel() { - return _cross_level; - } - void SetGhost(bool set) { - _ghost = set; - } - bool Attached() { - return _attached; - } - -protected: - Variable *_variable; - bool _ghost; - bool _cross_level; - bool _attached; // to a valve for flows -}; -class VensimValveElement : public VensimViewElement { -public: - ElementType Type() override { - return ElementTypeVALVE; - } - VensimValveElement(char *curpos, char *buf, VensimParse *parser); - bool Attached() { - return _attached; - } - -private: - bool _attached; -}; -class VensimCommentElement : public VensimViewElement { -public: - ElementType Type() override { - return ElementTypeCOMMENT; - } - VensimCommentElement(char *curpos, char *buf, VensimParse *parser); -}; -class VensimConnectorElement : public VensimViewElement { -public: - ElementType Type() override { - return ElementTypeCONNECTOR; - } - VensimConnectorElement(char *curpos, char *buf, VensimParse *parser); - VensimConnectorElement(int from, int to, int x, int y); - virtual bool ScalePoints(double xratio, double yratio, int offx, int offy) override; - int From() { - return _from; - } - int To() { - return _to; - } - void Invalidate() { - _to = _from = 0; - } - bool FromAsAlias(); // in this case From will send back a number - char Polarity() const { - return _polarity; - } - -private: - int _from; - int _to; - int _npoints; - char _polarity; -}; - -class VensimView : public View { -public: - ~VensimView() { - // Clean up dynamically allocated elements - for (auto *element : vElements) { - delete element; - } - vElements.clear(); - } - - const std::string &Title() { - return sTitle; - } - void SetTitle(const std::string &title) { - sTitle = title; - } - void ReadView(VensimParse *parser, char *buf); - int GetNextUID(); - VensimViewElements &Elements() { - return vElements; - } - virtual bool empty() const override; - - virtual bool UpgradeGhost(Variable *var) override; - virtual bool AddFlowDefinition(Variable *var, Variable *upstream, Variable *downstream) override; - virtual bool AddVarDefinition(Variable *var, int x, int y) override; - virtual void CheckGhostOwners() override; - virtual void CheckLinksIn() override; - bool FindInArrow(Variable *source, int target); - void RemoveExtraArrowsIn(std::vector ins, int target); - int FindVariable(Variable *in, int x, int y); // add if necessary - returns UID - - int SetViewStart(int x, int y, double xratio, double yratio, int uid); // returns last uid val + 1 - int GetViewMaxX(int defval); - int GetViewMaxY(int defval); - int UIDOffset() { - return _uid_offset; - } - -private: - VensimViewElements vElements; - std::string sTitle; - int _uid_offset; -}; - -#endif diff --git a/src/xmutil/third_party/xmutil/XMUtil.cpp b/src/xmutil/third_party/xmutil/XMUtil.cpp deleted file mode 100644 index 764d84b2f..000000000 --- a/src/xmutil/third_party/xmutil/XMUtil.cpp +++ /dev/null @@ -1,341 +0,0 @@ -// XMUtil.cpp : Defines the entry point for the console application. -// -#include "XMUtil.h" - -#include -#include - -#include "Dynamo/DynamoParse.h" -#include "Model.h" -#include "Unicode.h" -#include "Vensim/VensimParse.h" - -#ifdef WITH_UI -#include - -#include "UI/Main_Window.h" -#endif - -std::string StringFromDouble(double val) { - char buf[32]; - auto result = std::to_chars(buf, buf + sizeof(buf), val); - return std::string(buf, result.ptr); -} - -std::string SpaceToUnderBar(const std::string &s) { - std::string rval; - for (const char *tv = s.c_str(); *tv; tv++) { - if (*tv == ' ') - rval.push_back('_'); - else - rval.push_back(*tv); - } - return rval; -} - -std::string QuotedSpaceToUnderBar(const std::string &s) { - std::string rval; - bool needquote = false; - for (const char *tv = s.c_str(); *tv; tv++) { - if (*tv == ' ') - rval.push_back('_'); - else { - if (*tv == '.') - needquote = true; - rval.push_back(*tv); - } - } - if (needquote) - rval = "\"" + rval + "\""; - return rval; -} - -bool StringMatch(const std::string &f, const std::string &s) { - if (f.size() != s.size()) - return false; - const char *tv1 = f.c_str(); - const char *tv2 = s.c_str(); - char c1, c2; - for (; (c1 = *tv1); tv1++, tv2++) { - c2 = *tv2; - if (c1 != c2) { - if (c1 >= 'A' && c1 <= 'Z') - c1 += ('a' - 'A'); - if (c2 >= 'A' && c2 <= 'Z') - c2 += ('a' - 'A'); - if (c1 != c2) - return false; - } - } - return true; -} - -double AngleFromPoints(double startx, double starty, double pointx, double pointy, double endx, double endy) { - double thetax; - if (endx > startx) - thetax = -atan((endy - starty) / (endx - startx)) * 180 / 3.14159265358979; - else if (endx < startx) - thetax = 180 - atan((starty - endy) / (startx - endx)) * 180 / 3.14159265358979; - else if (endy > starty) - thetax = 270; - else - thetax = 90; - // straight line connector- use this if geometry fails - if (pointx == 0 && pointy == 0) - return thetax; - - // first take the start and end point - the center of the circle is on a line perpindicular - // to the line between them and intersects it at its midpoint - double line1x = (startx + endx) / 2; - double line1y = (starty + endy) / 2; - double slope1x, slope1y; - if (startx == endx) { - slope1x = 1; - slope1y = 0; - } else if (starty == endy) { - slope1x = 0; - slope1y = 1; - } else { - slope1x = endy - starty; // perpindicular - flip xy - slope1y = startx - endx; // flip the sign - } - // next do point and end - most likely to have good numerics - double line2x = (pointx + endx) / 2; - double line2y = (pointy + endy) / 2; - double slope2x, slope2y; - if (pointx == endx) { - slope2x = 1; - slope2y = 0; - } else if (pointy == endy) { - slope2x = 0; - slope2y = 1; - } else { - slope2x = endy - pointy; // perpindicular - flip xy - slope2y = pointx - endx; // flip the sign - } - /* now we solve for delta1 and delta2 such that - line1y + delta1 * slope1y = line2y + delta2 * slope2y - line1x + delta1 * slope1x = line2x + delta2 * slope2x - */ - double delta1, delta2; - if (slope1y == 0) { - if (slope2y == 0 || slope1x == 0) - return thetax; - delta2 = (line1y - line2y) / slope2y; - delta1 = (line2x + delta2 * slope2x - line1x) / slope1x; - } else if (slope1x == 0) { - if (slope2x == 0) - return thetax; - delta2 = (line1x - line2x) / slope2x; - delta1 = (line2y + delta2 * slope2y - line1y) / slope1y; - } else if (slope2y == 0) { - if (slope2x == 0) - return thetax; - delta1 = (line2y - line1y) / slope1y; - delta2 = (line1x + delta1 * slope1x - line2x) / slope2x; - } else { - /* now we solve for delta1 and delta2 such that - line1y + delta1 * slope1y = line2y + delta2 * slope2y - -> delta1 = (line2y + delta2 * slope2y - line1y)/slope1y - line1x + delta1 * slope1x = line2x + delta2 * slope2x - -> line1x + (line2y + delta2 * slope2y - line1y)/slope1y * slope1x = line2x + delta2 * slope2x - -> line1x + (line2y - line1y)/slope1y * slope1x - line2x = delta2 * (slope2x - slope1x*slope2y/slope1y) - -> - */ - if (abs(slope2x - slope1x * slope2y / slope1y) < 1e-8) - return thetax; - delta2 = (line1x + (line2y - line1y) / slope1y * slope1x - line2x) / (slope2x - slope1x * slope2y / slope1y); - delta1 = (line2y + delta2 * slope2y - line1y) / slope1y; - } - double centerx = line1x + delta1 * slope1x; - double centery = line1y + delta1 * slope1y; - assert(line2x + delta2 * slope2x - centerx < 1e-8); - assert(line2y + delta2 * slope2y - centery < 1e-8); - // arc tan of slope perpeindicular to center start line - if (abs(centery - starty) < 1e-6) { - if (pointy > starty) - return 90; - return 270; - } - if (abs(centerx - startx) < 1e-6) { - if (pointx > startx) - return 0; - return 180; - } - thetax = atan2(-(starty - centery), (startx - centerx)) * 180 / 3.14159265358979; - // this needs to go through the point - so add or subtract 90 to get o - // find the angle closest to the angle from start to point - - double direct = atan2(-(pointy - starty), (pointx - startx)) * 180 / 3.14159265358979; - double diff1 = direct - (thetax - 90); - while (diff1 < 0) - diff1 += 360; - while (diff1 > 180) - diff1 -= 360; - double diff2 = direct - (thetax + 90); - while (diff2 < 0) - diff2 += 360; - while (diff2 > 180) - diff2 -= 360; - if (abs(diff1) < abs(diff2)) - thetax -= 90; - else - thetax += 90; - return thetax; - - if (abs(pointx - startx) > abs(pointy - starty)) { - if (pointx < startx) { - // need to end up in quadrant 2 or 3 - if (thetax >= 0) // in 1 or 2 - thetax += 90; - else // in 3 or 4 - thetax -= 90; - } else // need to end up in quadrant 1 or 4 - { - if (thetax >= 0) // in 1 or 2 - thetax -= 90; - else // in 3 or 4 - thetax += 90; - } - } else { - if (pointy < starty) { - // need to end up in quadrant 3 or 4 - if (thetax >= 0) // in 1 or 2 - { - if (thetax < 90) // 1 - thetax -= 90; - else - thetax += 90; - } else // in 3 or 4 - { - if (thetax < -90) - thetax -= 90; - else - thetax += 90; - } - } else // need to end up in quadrant 1 or 2 - { - if (thetax >= 0) // in 1 or 2 - { - if (thetax < 90) // 1 - thetax += 90; - else - thetax -= 90; - } else // in 3 or 4 - { - if (thetax < -90) - thetax += 90; - else - thetax -= 90; - } - } - } - return thetax; - - // below is wrong - we need to triangulate to get the center then pull out the tangent at the start point - - // case point between start and end - double a2 = (pointx - startx) * (pointx - startx) + (pointy - starty) * (pointy - starty); - double b2 = (pointx - endx) * (pointx - endx) + (pointy - endy) * (pointy - endy); - double c2 = (startx - endx) * (startx - endx) + (starty - endy) * (starty - endy); - double x = (c2 + (a2 - b2)) / (2 * sqrt(c2)); - double y2 = a2 - x * x; - double theta = atan(sqrt(y2) / x); - if (!std::isnan(theta)) - return theta * 180 / 3.141592676; - theta = atan((endy - starty) / (endx - startx)); - if (!std::isnan(theta)) - return theta * 180 / 3.141592676; - if (endy < starty) - return 90; - return 270; - return 33; -} - -extern "C" { -// returns NULL on error or a string containing XMILE that the caller now owns -char *xmutil_convert_mdl_to_xmile(const char *mdlSource, uint32_t mdlSourceLen, const char *fileName, bool isCompact, - int isLongName, bool isAsSectors) { - try { - Model m{}; - std::string ext; - if (fileName == nullptr) { - fileName = ""; - } else if (strlen(fileName) > 5) - ext = fileName + strlen(fileName) - 3; - - // parse the input - double xscale = 1.0; - double yscale = 1.0; - if (ext == "dyn" || ext == "DYN") { - DynamoParse dp{&m}; - dp.SetLongName(isLongName != 0); - m.SetAsSectors(isAsSectors); - if (!dp.ProcessFile(fileName, mdlSource, mdlSourceLen)) { - return nullptr; - } - xscale = dp.Xratio(); - yscale = dp.Yratio(); - } else { - VensimParse vp{&m}; - vp.SetLongName(isLongName == 1); - m.SetAsSectors(isAsSectors); - if (!vp.ProcessFile(fileName, mdlSource, mdlSourceLen)) { - return nullptr; - } - xscale = vp.Xratio(); - yscale = vp.Yratio(); - } - - // if(m->AnalyzeEquations()) { - // m->Simulate() ; - // m->OutputComputable(true); - // } - - // mark variable types and potentially convert INTEG equations - // involving expressions into flows (a single net flow on the first - // pass though this) - m.MarkVariableTypes(nullptr); - m.AdjustGroupNames(); // have to be unique and not elsewhere in the model - - for (MacroFunction *mf : m.MacroFunctions()) { - m.MarkVariableTypes(mf->NameSpace()); - } - - // any ghosts that are never defined make the first appearance not a ghost - m.CheckGhostOwners(); - - // if there is a view then try to make sure everything is defined in - // the views put unknowns in a heap in the first view at 20,20 but - // for things that have connections try to put them in the right - // place - bool want_complete = false; // could pass this as an option - but let the reader handle this stuff - if (want_complete) { - m.AttachStragglers(); - } - - // Collect any errors produced while generating XMILE and log them - std::vector errs; - std::string xmile = m.PrintXMILE(isCompact, errs, xscale, yscale); - - if (!errs.empty()) { - for (const auto &e : errs) { - log("XMILE generation error: %s\n", e.c_str()); - } - return nullptr; - } - - char *result = strdup(xmile.c_str()); - - return result; - } catch (const std::exception &e) { - // Log the exception for debugging but don't let it escape to C caller - log("Exception caught in xmutil_convert_mdl_to_xmile: %s\n", e.what()); - return nullptr; - } catch (...) { - // Catch any other exceptions to protect C callers - log("Unknown exception caught in xmutil_convert_mdl_to_xmile\n"); - return nullptr; - } -} -} // extern "C" diff --git a/src/xmutil/third_party/xmutil/XMUtil.h b/src/xmutil/third_party/xmutil/XMUtil.h deleted file mode 100644 index 3ab5ede3d..000000000 --- a/src/xmutil/third_party/xmutil/XMUtil.h +++ /dev/null @@ -1,86 +0,0 @@ -#ifndef _XMUTIL_XMUTIL_H -#define _XMUTIL_XMUTIL_H - -#include -#include -#include - -#include "Log.h" - -#ifdef WIN32 -// XMUtil.h - globally included - generally for help with -// memory leak detection -// -#if defined(rollyourown) && defined(_DEBUG) && defined(__cplusplus) -#include -extern void AddTrack(void *p, size_t size, const char *file, int line); -extern void RemoveTrack(void *p); -/* specialized placement new to track allocations */ -inline void *__cdecl operator new(size_t size, const char *file, int line) { - void *ptr = (void *)malloc(size); - if (!ptr) - throw std::bad_alloc(); - AddTrack(ptr, size, file, line); - return (ptr); -}; -inline void *__cdecl operator new[](size_t size, const char *file, int line) { - void *ptr = (void *)malloc(size); - if (!ptr) - throw std::bad_alloc(); - AddTrack(ptr, size, file, line); - return (ptr); -}; -/* matching placement delete for exception handling */ -inline void __cdecl operator delete(void *p, const char *file, int line) { - RemoveTrack(p); - free(p); -} -inline void __cdecl operator delete[](void *p, const char *file, int line) { - RemoveTrack(p); - free(p); -} -inline void __cdecl operator delete(void *p) { - RemoveTrack(p); - free(p); -}; -inline void __cdecl operator delete[](void *p) { - RemoveTrack(p); - free(p); -}; - -#define XDEBUG_NEW new (__FILE__, __LINE__) -#define new XDEBUG_NEW -#define XDEBUG_DELETE delete -#define delete XDEBUG_DELETE -#elif defined(_DEBUG) -#define _CRTDBG_MAP_ALLOC -#include -#include -#define DBG_NEW new (_CLIENT_BLOCK, __FILE__, __LINE__) -#define new DBG_NEW -#endif -#endif - -#ifdef WIN32 -#define XMUTIL_EXPORT -#else -#define XMUTIL_EXPORT __attribute__((visibility("default"))) -#endif - -extern "C" { -// returns NULL on error or a string containing XMILE that the caller now owns -XMUTIL_EXPORT char *xmutil_convert_mdl_to_xmile(const char *mdlSource, uint32_t mdlSourceLen, const char *fileName, - bool isCompact, int isLongName, bool isAsSectors); -// returns a non-owned, null-terminated C-string with any log output from -// previous xmutil_convert_mdl_to_xmile invocations -XMUTIL_EXPORT const char *xmutil_get_log(void); -XMUTIL_EXPORT void xmutil_clear_log(void); -} - -// utility functions -std::string StringFromDouble(double val); -std::string SpaceToUnderBar(const std::string &s); -std::string QuotedSpaceToUnderBar(const std::string &s); -bool StringMatch(const std::string &f, const std::string &s); // asciii only; -double AngleFromPoints(double startx, double starty, double pointx, double pointy, double endx, double endy); -#endif diff --git a/src/xmutil/third_party/xmutil/Xmile/XMILEGenerator.cpp b/src/xmutil/third_party/xmutil/Xmile/XMILEGenerator.cpp deleted file mode 100644 index f18e39300..000000000 --- a/src/xmutil/third_party/xmutil/Xmile/XMILEGenerator.cpp +++ /dev/null @@ -1,1130 +0,0 @@ -#include "XMILEGenerator.h" - -#include - -#include "../Model.h" -#include "../Symbol/ExpressionList.h" -#include "../Vensim/VensimView.h" -#include "../XMUtil.h" - -XMILEGenerator::XMILEGenerator(Model *model, double xratio, double yratio, bool from_dynamo) { - _model = model; - _xratio = xratio; - _yratio = yratio; - _from_dynamo = from_dynamo; -} - -std::string XMILEGenerator::Print(bool is_compact, std::vector &errs, bool as_sectors) { - tinyxml2::XMLDocument doc; - - tinyxml2::XMLElement *root = doc.NewElement("xmile"); - root->SetName("xmile"); - root->SetAttribute("xmlns", "http://docs.oasis-open.org/xmile/ns/XMILE/v1.0"); - root->SetAttribute("xmlns:isee", "http://iseesystems.com/XMILE"); - root->SetAttribute("version", "1.0"); - doc.InsertFirstChild(root); - - tinyxml2::XMLElement *prefs = doc.NewElement("isee:prefs"); - root->InsertEndChild(prefs); - prefs->SetAttribute("show_module_prefix", "true"); - prefs->SetAttribute("layer", "model"); - - tinyxml2::XMLElement *header = doc.NewElement("header"); - this->generateHeader(header, errs); - root->InsertEndChild(header); - - tinyxml2::XMLElement *specs = doc.NewElement("sim_specs"); - this->generateSimSpecs(specs, errs); - root->InsertEndChild(specs); - - tinyxml2::XMLElement *model_units = doc.NewElement("model_units"); - this->generateModelUnits(model_units, errs); - root->InsertEndChild(model_units); - - tinyxml2::XMLElement *dimensions = doc.NewElement("dimensions"); - this->generateDimensions(dimensions, errs); - root->InsertEndChild(dimensions); - _model->MakeViewNamesUnique(); - if (as_sectors) { - tinyxml2::XMLElement *model = doc.NewElement("model"); - this->generateModelAsSectors(model, errs, NULL, true); - root->InsertEndChild(model); - } else - this->generateModelAsModules(root, errs, NULL); - - // macros are presented as separate models - for (MacroFunction *mf : _model->MacroFunctions()) { - tinyxml2::XMLElement *macro = doc.NewElement("macro"); - macro->SetAttribute("name", mf->GetName().c_str()); - - // in vensim the equation is always just the name fo the macro - tinyxml2::XMLElement *xeqn = doc.NewElement("eqn"); - macro->InsertEndChild(xeqn); - xeqn->SetText(mf->GetName().c_str()); - // the parms are all of the entries in the macro description - ExpressionList *args = mf->Args(); - int n = args->Length(); - for (int i = 0; i < n; i++) { - tinyxml2::XMLElement *xparm = doc.NewElement("parm"); - macro->InsertEndChild(xparm); - Expression *pexp = args->GetExp(i); - ContextInfo info(NULL); - pexp->OutputComputable(&info); - xparm->SetText(info.str().c_str()); - } - - this->generateModelAsSectors(macro, errs, mf->NameSpace(), false); // not really a secotr - only a root module here - root->InsertEndChild(macro); - } - - tinyxml2::XMLPrinter printer{nullptr, is_compact}; - if (!doc.Accept(&printer)) { - if (doc.ErrorStr()) { - errs.push_back("TinyXML2 Error: " + std::string(doc.ErrorStr())); - } - return ""; - } - - std::string xmile = printer.CStr(); - - return xmile; -} - -void XMILEGenerator::generateHeader(tinyxml2::XMLElement *element, std::vector &errs) { - tinyxml2::XMLDocument *doc = element->GetDocument(); - - tinyxml2::XMLElement *options = doc->NewElement("options"); - options->SetAttribute("namespace", "std"); - element->InsertEndChild(options); - - if (!_from_dynamo) { - tinyxml2::XMLElement *vendor = doc->NewElement("vendor"); - vendor->SetText("Ventana Systems, xmutil"); - element->InsertEndChild(vendor); - } - - tinyxml2::XMLElement *product = doc->NewElement("product"); - product->SetAttribute("lang", "en"); - if (_from_dynamo) - product->SetText("Dynamo, xmutil"); - else - product->SetText("Vensim, xmutil"); - element->InsertEndChild(product); -} - -void XMILEGenerator::generateSimSpecs(tinyxml2::XMLElement *element, std::vector &errs) { - /* - - 0< / start> - 100< / stop> -
0.125< / dt> - < / sim_specs> - */ - - tinyxml2::XMLDocument *doc = element->GetDocument(); - - if (_model->IntegrationType() == Integration_Type_RK4) - element->SetAttribute("method", "RK4"); - else if (_model->IntegrationType() == Integration_Type_RK2) - element->SetAttribute("method", "RK2"); - else - element->SetAttribute("method", "Euler"); - - UnitExpression *uexpr = _model->GetUnits("TIME STEP"); - if (!uexpr) - uexpr = _model->GetUnits("FINAL TIME"); - if (!uexpr) - uexpr = _model->GetUnits("INITIAL TIME"); - if (uexpr) - element->SetAttribute("time_units", uexpr->GetEquationString().c_str()); - else - element->SetAttribute("time_units", "Months"); - - double start = _model->GetConstanValue( - "INITIAL TIME", _model->initial_time()); // default to 0 if INITIAL TIME is missing or an equation - double stop = _model->GetConstanValue("FINAL TIME", _model->final_time()); - double dt = _model->GetConstanValue("TIME STEP", _model->dt()); - double saveper = _model->GetConstanValue("SAVEPER", dt); - double speed = _model->GetConstanValue("SIMULATION PAUSE", 0); - - if (start == -1) { - if (stop > 200) // this happens to work for national model - but hey - start = stop - 200; - else - start = 0; - } - if (stop <= start) - stop = start + 10 * dt; - - if (speed > 0) { - double duration = (stop - start) / saveper * speed; - char dur[32]; - snprintf(dur, sizeof(dur), "%g", duration); - element->SetAttribute("isee:sim_duration", dur); - } else - element->SetAttribute("isee:sim_duration", "0"); - - tinyxml2::XMLElement *startEle = doc->NewElement("start"); - startEle->SetText(StringFromDouble(start).c_str()); - element->InsertEndChild(startEle); - - tinyxml2::XMLElement *stopEle = doc->NewElement("stop"); - stopEle->SetText(StringFromDouble(stop).c_str()); - element->InsertEndChild(stopEle); - - tinyxml2::XMLElement *dtEle = doc->NewElement("dt"); - dtEle->SetText(StringFromDouble(dt).c_str()); - element->InsertEndChild(dtEle); - - if (saveper > dt) { - element->SetAttribute("isee:save_interval", std::to_string(saveper).c_str()); - } - - _model->SetUnwanted("INITIAL TIME", "STARTTIME"); - _model->SetUnwanted("FINAL TIME", "STOPTIME"); - _model->SetUnwanted("TIME STEP", "DT"); - _model->SetUnwanted("SAVEPER", "SAVEPER"); -} - -void XMILEGenerator::generateModelUnits(tinyxml2::XMLElement *element, std::vector &errs) { - /* - - -$/> - Dollars - $s - - - - */ - - tinyxml2::XMLDocument *doc = element->GetDocument(); - - std::vector &equivs = _model->UnitEquivs(); - - for (std::string &equiv : equivs) { - std::string name; - std::string eqn; - std::vector aliases; - const char *cur = equiv.c_str(); - while (*cur) { - const char *tv = cur; - while (true) { - if (*tv == ',' || !*tv) { - std::string cur_e(cur, tv - cur); - if (cur_e == "$") - eqn = cur_e; - else if (name.empty()) - name = cur_e; - else - aliases.push_back(cur_e); - if (*tv) - tv++; - break; - } - tv++; - } - cur = tv; - } - tinyxml2::XMLElement *xunit = doc->NewElement("unit"); - xunit->SetAttribute("name", name.c_str()); - if (!eqn.empty()) { - tinyxml2::XMLElement *xeqn = doc->NewElement("eqn"); - xeqn->SetText(eqn.c_str()); - xunit->InsertEndChild(xeqn); - } - for (std::string &alias : aliases) { - tinyxml2::XMLElement *xalias = doc->NewElement("alias"); - xalias->SetText(alias.c_str()); - xunit->InsertEndChild(xalias); - } - element->InsertEndChild(xunit); - } -} - -void XMILEGenerator::generateDimensions(tinyxml2::XMLElement *element, std::vector &errs) { - tinyxml2::XMLDocument *doc = element->GetDocument(); - std::vector vars = _model->GetVariables(); // all symbols that are variables - for (Variable *var : vars) { - if (var->VariableType() == XMILE_Type_ARRAY) { - // simple minded - defining equation - - Equation *eq = var->GetEquation(0); - if (eq) { - Expression *exp = eq->GetExpression(); - if (exp && exp->GetType() == EXPTYPE_Symlist) { - ExpressionSymbolList *esl = static_cast(exp); - SymbolList *symlist = esl->SymList(); - std::vector expanded; - int n = symlist->Length(); - for (int i = 0; i < n; i++) { - const SymbolList::SymbolListEntry &elm = (*symlist)[i]; - if (elm.eType == SymbolList::EntryType_SYMBOL) { - Equation::GetSubscriptElements(expanded, elm.u.pSymbol); - } - } - // we define subranges as if they were arrays themselves - because of the unique namespace in XMILE this - // is proper - and it will make any model with partial definitions more or less okay - - if (!expanded.empty() /* && expanded[0]->Owner() == var*/) { - tinyxml2::XMLElement *xsub = doc->NewElement("dim"); - xsub->SetAttribute("name", var->GetName().c_str()); - for (Symbol *s : expanded) { - tinyxml2::XMLElement *xelm = doc->NewElement("elem"); - xelm->SetAttribute("name", s->GetName().c_str()); - xsub->InsertEndChild(xelm); - } - // Check for dimension mapping (e.g., DimA: A1, A2, A3 -> DimB) - // The mapping info is in the ExpressionSymbolList's Map, which contains - // a SymbolList with the target dimension as the first symbol. - // - // There are two mapping forms in Vensim: - // 1. Simple: DimA: A1, A2, A3 -> DimB - // - mapList[0] is EntryType_SYMBOL with the target dimension - // 2. Explicit: DimA: A1, A2, A3 -> (DimB: B1, B2, B3) - // - mapList[0] is EntryType_LIST where the list's MapRange() has the target - SymbolList *mapList = esl->Map(); - if (mapList && mapList->Length() > 0) { - const SymbolList::SymbolListEntry &elm = (*mapList)[0]; - Symbol *mapTarget = nullptr; - - if (elm.eType == SymbolList::EntryType_SYMBOL) { - // Simple mapping: target is directly the symbol - mapTarget = elm.u.pSymbol; - } else if (elm.eType == SymbolList::EntryType_LIST) { - // Explicit mapping: target is stored in the list's MapRange - SymbolList *innerList = elm.u.pSymbolList; - if (innerList && innerList->MapRange()) { - mapTarget = innerList->MapRange(); - } - } - - if (mapTarget) { - tinyxml2::XMLElement *xmap = doc->NewElement("isee:maps_to"); - xmap->SetText(mapTarget->GetName().c_str()); - xsub->InsertEndChild(xmap); - } - } - element->InsertEndChild(xsub); - } - } - } - } - } -} - -// first pass if flat - we probably want to do this differently when we break up into modules -void XMILEGenerator::generateModelAsSectors(tinyxml2::XMLElement *element, std::vector &errs, - SymbolNameSpace *ns, bool want_diagram) { - tinyxml2::XMLDocument *doc = element->GetDocument(); - tinyxml2::XMLElement *variables = doc->NewElement("variables"); - element->InsertEndChild(variables); - - std::vector vars = _model->GetVariables(ns); // all symbols that are variables - for (Variable *var : vars) { - if (var->Unwanted()) - continue; - generateEquation(var, doc, variables); - } - if (want_diagram) { - tinyxml2::XMLElement *views = doc->NewElement("views"); - this->generateSectorViews(views, variables, errs, ns == NULL); - element->InsertEndChild(views); - } -} - -void XMILEGenerator::generateEquations(std::set &included, tinyxml2::XMLDocument *doc, - tinyxml2::XMLElement *variables) { - for (Variable *var : included) { - generateEquation(var, doc, variables); - } -} - -void XMILEGenerator::generateEquation(Variable *var, tinyxml2::XMLDocument *doc, tinyxml2::XMLElement *variables) { - XMILE_Type type = var->VariableType(); - std::string tag; - switch (type) { - case XMILE_Type_DELAYAUX: - case XMILE_Type_AUX: - tag = "aux"; - break; - case XMILE_Type_STOCK: - tag = "stock"; - break; - case XMILE_Type_FLOW: - tag = "flow"; - break; - case XMILE_Type_ARRAY: - return; - case XMILE_Type_ARRAY_ELM: - return; - default: - return; - break; - } - tinyxml2::XMLElement *xvar = doc->NewElement(tag.c_str()); - - variables->InsertEndChild(xvar); - xvar->SetAttribute("name", var->GetAlternateName().c_str()); - - if (type == XMILE_Type_DELAYAUX) { - tinyxml2::XMLElement *xcomment = doc->NewElement("isee:delay_aux"); - xvar->InsertEndChild(xcomment); - } - - std::vector eqns; - // for vensim models init equations will always be empty - std::vector init_eqns = var->GetAllInitEquations(); - bool wrap_init = false; - if (init_eqns.empty()) - eqns = var->GetAllEquations(); - else if (type == XMILE_Type_STOCK) - eqns.swap(init_eqns); // init_eqns is used for init values of aux otherwise - else { - eqns = var->GetAllEquations(); - if (eqns.empty()) { - eqns.swap(init_eqns); // dynamo convention can have N equations which should translate to INIT - wrap_init = true; - } - } - size_t eq_count = eqns.size(); - - // dimensions - std::vector elmlist; - int dim_count = var->SubscriptCountVars(elmlist); - - std::string comment = var->Comment(); - if (!comment.empty()) { - tinyxml2::XMLElement *xcomment = doc->NewElement("doc"); - xvar->InsertEndChild(xcomment); - xcomment->SetText(comment.c_str()); - } - if (type == XMILE_Type_STOCK) { - for (Variable *in : var->Inflows()) { - tinyxml2::XMLElement *inflow = doc->NewElement("inflow"); - xvar->InsertEndChild(inflow); - inflow->SetText(SpaceToUnderBar(in->GetAlternateName()).c_str()); - } - for (Variable *out : var->Outflows()) { - tinyxml2::XMLElement *outflow = doc->NewElement("outflow"); - xvar->InsertEndChild(outflow); - outflow->SetText(SpaceToUnderBar(out->GetAlternateName()).c_str()); - } - } - - tinyxml2::XMLElement *xelement = xvar; // usually these are the same - but for non a2a we have element entries - size_t eq_ind = 0; - size_t eq_pos = 0; - std::vector subs; // [ship,location] - std::vector> elms; // [s1,l1] - std::vector> entries; - std::vector dims; - while (eq_ind < eq_count) { - Equation *eqn = eqns[eq_ind]; - if (eq_count > 1) { - if (entries.empty()) - entries.resize(dim_count); - // we will blow up everything to single elements - if (elms.empty()) { - eq_pos = 0; - elms.clear(); - eqn->SubscriptExpand(elms, subs); - if (!elms.empty()) { - for (std::vector elm : elms) { - for (int i = 0; i < dim_count; i++) { - entries[i].insert(elm[i]); - } - } - } - } - if (!elms.empty()) { - dims = elms[eq_pos]; - std::string s; - int dim_count = dims.size(); - for (int j = 0; j < dim_count; j++) { - if (j) - s += ", "; - s += dims[j]->GetName(); - } - xelement = doc->NewElement("element"); - xelement->SetAttribute("subscript", s.c_str()); - xvar->InsertEndChild(xelement); - } - } - // skip it altogether if it is an A FUNCTION OF equation - std::string rhs = eqn->RHSFormattedXMILE(var, subs, dims, false); - if (eq_count <= 1 || rhs.size() < 42 || rhs.substr(28, 13) != "A FUNCTION OF") { - tinyxml2::XMLElement *xeqn = doc->NewElement("eqn"); - xelement->InsertEndChild(xeqn); - if (wrap_init) - rhs = "INIT(" + rhs + ")"; - xeqn->SetText(rhs.c_str()); - - // it it is active init we need to store that separately - if (eqn->IsActiveInit() || !init_eqns.empty()) { - tinyxml2::XMLElement *xieqn = doc->NewElement("init_eqn"); - xelement->InsertEndChild(xieqn); - if (eqn->IsActiveInit()) - xieqn->SetText(eqn->RHSFormattedXMILE(var, subs, dims, true).c_str()); - else { - // this will not work correctly for complicated situations - if (eq_ind < init_eqns.size()) - xieqn->SetText(init_eqns[eq_ind]->RHSFormattedXMILE(var, subs, dims, true).c_str()); - else - xieqn->SetText(init_eqns[0]->RHSFormattedXMILE(var, subs, dims, true).c_str()); - } - } - - // if it has a lookup we need to store that separately - ExpressionTable *et = eqn->GetTable(); - if (et) { - assert(type == XMILE_Type_AUX || type == XMILE_Type_FLOW); - std::vector *xvals = et->GetXVals(); - std::vector *yvals = et->GetYVals(); - tinyxml2::XMLElement *gf = doc->NewElement("gf"); - if (et->Extrapolate()) - gf->SetAttribute("type", "extrapolate"); - xelement->InsertEndChild(gf); - tinyxml2::XMLElement *yscale = doc->NewElement("yscale"); - gf->InsertEndChild(yscale); - tinyxml2::XMLElement *xpts = doc->NewElement("xpts"); - gf->InsertEndChild(xpts); - tinyxml2::XMLElement *ypts = doc->NewElement("ypts"); - gf->InsertEndChild(ypts); - - if (xvals->empty()) { - // most likely a translation error - fill in with 0,1 based on ysize - log("WARNING The graphical %s does not have a known x axis assuming 0,1", var->GetAlternateName().c_str()); - int n = yvals->size(); - if (n) { - double increment = 1.0 / (double)n; - double val = 0; - for (int i = 0; i < n; i++, val += increment) { - xvals->push_back(val); - } - } - } - std::string xstr; - for (size_t i = 0; i < xvals->size(); i++) { - if (i) - xstr += ","; - xstr += StringFromDouble((*xvals)[i]); - } - xpts->SetText(xstr.c_str()); - - std::string ystr; - double ymin = 0; - double ymax = 0; - for (size_t i = 0; i < yvals->size(); i++) { - if (i) { - ystr += ","; - if ((*yvals)[i] < ymin) - ymin = (*yvals)[i]; - else if ((*yvals)[i] > ymax) - ymax = (*yvals)[i]; - } else - ymin = ymax = (*yvals)[i]; - ystr += StringFromDouble((*yvals)[i]); - } - ypts->SetText(ystr.c_str()); - - if (ymin == ymax) - ymax = ymin + 1; - yscale->SetAttribute("min", StringFromDouble(ymin).c_str()); - yscale->SetAttribute("max", StringFromDouble(ymax).c_str()); - } - } - if (eq_count > 1) { - eq_pos++; - if (eq_pos >= elms.size()) { - elms.clear(); - eq_ind++; - } - } else - eq_ind++; - } - - // use entries to try to figure out the appropriate dimensions - if (dim_count) { - // Vensim allowed partial definition sets - XMILE uses subranges as separate dimensions so we - // try to find the most compact set of dimensions possible that inlcude all the equations include - std::vector dimensions; - - tinyxml2::XMLElement *xdims = doc->NewElement("dimensions"); - for (int i = 0; i < dim_count; i++) { - tinyxml2::XMLElement *xdim = doc->NewElement("dim"); - if (entries.empty()) { - // we might get a subrange in elmlist so need to get parent - but only if there is more than 1 equation - if (eq_count > 1 || elmlist[i]->GetAllEquations().empty()) - xdim->SetAttribute("name", elmlist[i]->Owner()->GetName().c_str()); - else - xdim->SetAttribute("name", elmlist[i]->GetName().c_str()); - } else { - std::set &entry = entries[i]; - Symbol *parent = (*entry.begin())->Owner(); - Symbol *best = parent; - if (parent->Subranges() != NULL && static_cast(parent)->Nelm() > entry.size()) { - for (Symbol *subrange : *parent->Subranges()) { - if (static_cast(subrange)->Nelm() >= entry.size() && - static_cast(subrange)->Nelm() < static_cast(best)->Nelm()) { - // does it have them all - bool complete = true; - std::vector telms; - Equation::GetSubscriptElements(telms, subrange); - for (Symbol *elm : entries[i]) { - if (std::find(telms.begin(), telms.end(), elm) == telms.end()) { - complete = false; - break; - } - } - if (complete) - best = subrange; - } - } - } - xdim->SetAttribute("name", best->GetName().c_str()); - } - xdims->InsertEndChild(xdim); - } - xvar->InsertEndChild(xdims); - } - - UnitExpression *un = var->Units(); - if (un) { - tinyxml2::XMLElement *units = doc->NewElement("units"); - xvar->InsertEndChild(units); - units->SetText(un->GetEquationString().c_str()); - } else if (!var->GetUnitsString().empty()) { - tinyxml2::XMLElement *units = doc->NewElement("units"); - xvar->InsertEndChild(units); - units->SetText(var->GetUnitsString().c_str()); - } -} - -void XMILEGenerator::generateModelAsModules(tinyxml2::XMLElement *element, std::vector &errs, - SymbolNameSpace *ns) { - // we will leave the base model completely empty and only generate the modules - letting the opening software - // lay out the modules and mark the connections between them - // we will also leave in anything that does not exist in any view at a reasonable level of - // attachment - std::vector &views = _model->Views(); - tinyxml2::XMLDocument *doc = element->GetDocument(); - if (views.size() < 2) { - if (generateModelAsGroups(element, errs, ns)) - return; - tinyxml2::XMLElement *model = doc->NewElement("model"); - generateModelAsSectors(model, errs, ns, true); - element->InsertEndChild(model); - return; - } - std::vector vars = _model->GetVariables(ns); // all symbols that are variables - // for every variable in a view, put its causes into the same view if they are not in any view - // otherwise we let the go cross level - for (Variable *var : vars) { - var->SetViewOfCauses(); - } - // and now for any variables still not assigned a view we do essentially the opposite - for (Variable *var : vars) { - var->SetViewToCause(5); - } - - tinyxml2::XMLElement *mainmodel = doc->NewElement("model"); - element->InsertEndChild(mainmodel); - tinyxml2::XMLElement *modules = doc->NewElement("variables"); - mainmodel->InsertEndChild(modules); - int uid_off = 0; - - for (View *gview : views) { - if (gview->empty()) - continue; - VensimView *view = static_cast(gview); - tinyxml2::XMLElement *submodel = doc->NewElement("model"); - assert(!view->Title().empty()); - submodel->SetAttribute("name", view->Title().c_str()); - element->InsertEndChild(submodel); - - tinyxml2::XMLElement *variables = doc->NewElement("variables"); - submodel->InsertEndChild(variables); - // first we get a list of variables included in the view and all of their inputs - if an - // input is not in the view we need to make a ghost to hold its place - std::set included; - for (Variable *var : vars) { - if (var->Unwanted()) - continue; - if (var->GetView() == gview) // wanted normal - included.insert(var); - } - std::set needed; - for (Variable *var : included) { - std::vector inputs = var->GetInputVars(); - for (Variable *input : inputs) { - if (included.find(input) == included.end() && input->VariableType() != XMILE_Type_ARRAY && - input->VariableType() != XMILE_Type_ARRAY_ELM && input->VariableType() != XMILE_Type_UNKNOWN && - !StringMatch(input->GetName(), "Time") && !input->Unwanted()) { - needed.insert(input); - } - } - } - - // needed will be cross level - // add this as a module to the main model - tinyxml2::XMLElement *module = doc->NewElement("module"); - module->SetAttribute("name", view->Title().c_str()); - modules->InsertEndChild(module); - // and mark the cross levels - for (Variable *var : needed) { - tinyxml2::XMLElement *connect = doc->NewElement("connect"); - std::string to = view->Title() + "." + var->GetAlternateName(); - connect->SetAttribute("to", SpaceToUnderBar(to).c_str()); - std::string from; - if (var->GetView()) - from = static_cast(var->GetView())->Title(); - from += "." + var->GetAlternateName(); - connect->SetAttribute("from", SpaceToUnderBar(from).c_str()); - module->InsertEndChild(connect); - } - generateEquations(included, doc, variables); - // for the incomming cross levels they get no equations but need to be included as variables - for (Variable *var : needed) { - tinyxml2::XMLElement *xvar; - if (var->VariableType() == XMILE_Type_STOCK) - xvar = doc->NewElement("stock"); - else - xvar = doc->NewElement("aux"); - - variables->InsertEndChild(xvar); - xvar->SetAttribute("name", var->GetAlternateName().c_str()); - xvar->SetAttribute("access", "input"); - } - - tinyxml2::XMLElement *xviews = doc->NewElement("views"); - submodel->InsertEndChild(xviews); - tinyxml2::XMLElement *xview = doc->NewElement("view"); - if (_model->LetterPolarity()) - xview->SetAttribute("isee:use_lettered_polarity", "true"); - xviews->InsertEndChild(xview); - uid_off = view->SetViewStart(100, 100, _xratio, _yratio, uid_off); - this->generateView(view, xview, errs, &needed); - } - std::set remnant; - for (Variable *var : vars) { - if (var->GetView() == NULL && !var->Unwanted()) - remnant.insert(var); - } - generateEquations(remnant, doc, modules); - // tinyxml2::XMLElement* views = doc->NewElement("views"); - // this->generateSectorViews(views, variables, errs, ns == NULL); - // element->InsertEndChild(views); -} - -bool XMILEGenerator::generateModelAsGroups(tinyxml2::XMLElement *element, std::vector &errs, - SymbolNameSpace *ns) { - tinyxml2::XMLDocument *doc = element->GetDocument(); - std::vector &groups = _model->Groups(); - int used_groups = 0; - for (ModelGroup *group : groups) { - if (!group->vVariables.empty()) - used_groups++; - } - if (used_groups < 2) - return false; - - groups.push_back(new ModelGroup("Other not Grouped", NULL)); - // we use addess of group for simplicity - std::vector vars = _model->GetVariables(ns); // all symbols that are variables - // for every variable in a view, put its causes into the same view if they are not in any view - // otherwise we let the go cross level - for (Variable *var : vars) { - if (!var->GetGroup() && var->VariableType() != XMILE_Type_ARRAY && var->VariableType() != XMILE_Type_ARRAY_ELM && - var->VariableType() != XMILE_Type_UNKNOWN) { - var->SetGroup(groups.back()); - groups.back()->vVariables.push_back(var); - } - } - // all vars are now grouped - - tinyxml2::XMLElement *mainmodel = doc->NewElement("model"); - element->InsertEndChild(mainmodel); - tinyxml2::XMLElement *mainvariables = doc->NewElement("variables"); - mainmodel->InsertEndChild(mainvariables); - for (ModelGroup *group : groups) { - if (!group->vVariables.empty() && group->pOwner == NULL) { - group->pModule = doc->NewElement("module"); - group->pModule->SetAttribute("name", group->sName.c_str()); - mainvariables->InsertEndChild(group->pModule); - } - } - - // set up the module constucts - these need to be filled in before any cross levels are created - for (ModelGroup *group : groups) { - if (group->vVariables.empty()) - continue; - group->pModel = doc->NewElement("model"); - assert(!group->sName.empty()); - group->pModel->SetAttribute("name", group->sName.c_str()); - element->InsertEndChild(group->pModel); - group->pVariables = doc->NewElement("variables"); - group->pModel->InsertEndChild(group->pVariables); - // find any modules that we own - for (ModelGroup *sgroup : groups) { - if (sgroup->pOwner == group) { - sgroup->pModule = doc->NewElement("module"); - sgroup->pModule->SetAttribute("name", sgroup->sName.c_str()); - group->pVariables->InsertEndChild(sgroup->pModule); - } - } - } - // now output cross levels and actual variables - for (ModelGroup *group : groups) { - if (group->vVariables.empty()) - continue; - // first we get a list of variables included in the view and all of their inputs - if an - // input is not in the view we need to make a ghost to hold its place - std::set included; - for (Variable *var : group->vVariables) { - if (var->Unwanted()) - continue; - included.insert(var); - } - std::set needed; - for (Variable *var : included) { - std::vector inputs = var->GetInputVars(); - for (Variable *input : inputs) { - if (included.find(input) == included.end() && input->VariableType() != XMILE_Type_ARRAY && - input->VariableType() != XMILE_Type_ARRAY_ELM && input->VariableType() != XMILE_Type_UNKNOWN && - !StringMatch(input->GetName(), "Time") && !input->Unwanted()) { - needed.insert(input); - } - } - inputs = var->GetInitInputVars(); - for (Variable *input : inputs) { - if (included.find(input) == included.end() && input->VariableType() != XMILE_Type_ARRAY && - input->VariableType() != XMILE_Type_ARRAY_ELM && input->VariableType() != XMILE_Type_UNKNOWN && - !StringMatch(input->GetName(), "Time") && !input->Unwanted()) { - needed.insert(input); - } - } - } - - // needed will be cross level - - // and mark the cross levels - for (Variable *var : needed) { - tinyxml2::XMLElement *connect = doc->NewElement("connect"); - std::string to = group->sName + "." + var->GetAlternateName(); - connect->SetAttribute("to", SpaceToUnderBar(to).c_str()); - std::string from; - from = var->GetGroup()->sName; - from += "." + var->GetAlternateName(); - connect->SetAttribute("from", SpaceToUnderBar(from).c_str()); - var->GetGroup()->pModule->InsertEndChild(connect); // receiving module - } - generateEquations(included, doc, group->pVariables); - // for the incomming cross levels they get no equations but need to be included as variables - for (Variable *var : needed) { - tinyxml2::XMLElement *xvar; - if (var->VariableType() == XMILE_Type_STOCK) - xvar = doc->NewElement("stock"); - else - xvar = doc->NewElement("aux"); - - group->pVariables->InsertEndChild(xvar); - xvar->SetAttribute("name", var->GetAlternateName().c_str()); - xvar->SetAttribute("access", "input"); // let the receiving softwwre take care out output access - } - } - return true; -} - -void XMILEGenerator::generateSectorViews(tinyxml2::XMLElement *element, tinyxml2::XMLElement *xvars, - std::vector &errs, bool mainmodel) { - tinyxml2::XMLDocument *doc = element->GetDocument(); - - std::vector &views = _model->Views(); - if (views.empty() && mainmodel) { - std::vector &groups = _model->Groups(); - if (!groups.empty()) { - for (ModelGroup *group : groups) { - tinyxml2::XMLElement *xgroup = doc->NewElement("group"); - xgroup->SetAttribute("name", group->sName.c_str()); - if (group->pOwner) - xgroup->SetAttribute("owner", group->pOwner->sName.c_str()); - element->InsertEndChild(xgroup); - for (Variable *var : group->vVariables) { - tinyxml2::XMLElement *xvar = doc->NewElement("var"); - xvar->SetText(SpaceToUnderBar(var->GetAlternateName()).c_str()); - xgroup->InsertEndChild(xvar); - } - } - } - return; - } - int x, y; - // start at a reasonable distance from 0 - the x,y values are generally around hte center - // of the var - x = 100; - y = 100; - // all the views against a single xmile view - or break up into modules - need vector of models as input to do that - tinyxml2::XMLElement *xview = doc->NewElement("view"); - if (_model->LetterPolarity()) - xview->SetAttribute("isee:use_lettered_polarity", "true"); - element->InsertEndChild(xview); - int uid_off = 0; - for (View *gview : views) { - VensimView *view = static_cast(gview); - // first update geometry - we put views one after another along the y axix - could lay out in pages or something - uid_off = view->SetViewStart(x, y + 20, _xratio, _yratio, uid_off); - int width = view->GetViewMaxX(100); - int height = view->GetViewMaxY(y + 80) - y; - // add a surrounding sector to contain this view - call it the view name - // - - if (views.size() > 1) { - std::string name = view->Title(); - tinyxml2::XMLElement *xsectorvar = doc->NewElement("group"); - xvars->InsertEndChild(xsectorvar); - xsectorvar->SetAttribute("name", name.c_str()); - tinyxml2::XMLElement *xsector = doc->NewElement("group"); - xview->InsertEndChild(xsector); - xsector->SetAttribute("name", name.c_str()); - xsector->SetAttribute("x", StringFromDouble(x - 40).c_str()); - xsector->SetAttribute("y", StringFromDouble(y).c_str()); - xsector->SetAttribute("width", StringFromDouble(width + 60).c_str()); - xsector->SetAttribute("height", StringFromDouble(height + 40).c_str()); - } - - y += height + 80; - - this->generateView(view, xview, errs, NULL); - } -} - -void XMILEGenerator::generateView(VensimView *view, tinyxml2::XMLElement *element, std::vector &errs, - std::set *adds) { - tinyxml2::XMLDocument *doc = element->GetDocument(); - int uid = view->UIDOffset(); - int local_uid = 0; - VensimViewElements &elements = view->Elements(); - for (VensimViewElement *ele : elements) { - if (ele) { - if (ele->Type() == VensimViewElement::ElementTypeVARIABLE) { - assert(ele->X() > 0 && ele->Y() > 0); - VensimVariableElement *vele = static_cast(ele); - Variable *var = vele->GetVariable(); - // skip time altogether - this never shows up under xmil - if (!var || StringMatch(vele->GetVariable()->GetName(), "Time") || var->Unwanted()) - ; // do nothing - else if (vele->Ghost(adds)) { - assert(vele->GetVariable()->VariableType() != XMILE_Type_ARRAY); - tinyxml2::XMLElement *xghost = doc->NewElement("alias"); - element->InsertEndChild(xghost); - xghost->SetAttribute("x", vele->X()); - xghost->SetAttribute("y", vele->Y()); - if (vele->GetVariable() && vele->GetVariable()->VariableType() == XMILE_Type_STOCK) { - xghost->SetAttribute("x", vele->X() - 22); - xghost->SetAttribute("y", vele->Y() - 17); - xghost->SetAttribute("width", 45); - xghost->SetAttribute("height", 35); - } else { - xghost->SetAttribute("x", vele->X()); - xghost->SetAttribute("y", vele->Y()); - } - xghost->SetAttribute("uid", uid); - tinyxml2::XMLElement *xof = doc->NewElement("of"); - xghost->InsertEndChild(xof); - xof->SetText(SpaceToUnderBar(vele->GetVariable()->GetAlternateName()).c_str()); - } else { - XMILE_Type type = vele->GetVariable()->VariableType(); - std::string tag; - switch (type) { - case XMILE_Type_DELAYAUX: - case XMILE_Type_AUX: - tag = "aux"; - break; - case XMILE_Type_STOCK: - tag = "stock"; - break; - case XMILE_Type_FLOW: - tag = "flow"; - break; - default: - log("unknown view element type %d\n", type); - } - if (tag.empty()) - continue; - tinyxml2::XMLElement *xvar = doc->NewElement(tag.c_str()); - - element->InsertEndChild(xvar); - - std::string name = vele->GetVariable()->GetAlternateName(); - xvar->SetAttribute("name", SpaceToUnderBar(vele->GetVariable()->GetAlternateName()).c_str()); - if (type == XMILE_Type_FLOW && vele->Attached() && elements[local_uid - 1] && - elements[local_uid - 1]->Type() == VensimViewElement::ElementTypeVALVE) { - xvar->SetAttribute("x", elements[local_uid - 1]->X()); - xvar->SetAttribute("y", elements[local_uid - 1]->Y()); - } else { - // pretty big things - Vensim's default size is 80x40 - width and height are half vals so a fair bit bigger - // 90x50 then bring size across - if (type == XMILE_Type_STOCK && !vele->CrossLevel() && !vele->Ghost(NULL) && - (vele->Width() > 45 || vele->Height() > 25)) { - int x = vele->X(); - int y = vele->Y(); - int width = 2 * vele->Width(); - int height = 2 * vele->Height(); - if (width < 60) - width = 60; - if (height < 40) - height = 40; - x -= width / 2; - y -= height / 2; - xvar->SetAttribute("x", x); - xvar->SetAttribute("y", y); - xvar->SetAttribute("width", width); - xvar->SetAttribute("height", height); - } else { - xvar->SetAttribute("x", vele->X()); - xvar->SetAttribute("y", vele->Y()); - } - } - if (type == XMILE_Type_FLOW) { - // need points - these are the location of the from and to - no matter what they are - // but we need to search through the list of eleemnts to find the from and to - flow - // arrows are always out of the attached value which is just before us in the list - // flow direction we need to take from the model proper - arbitrary if flow is not connected - size_t n = elements.size(); - int count = 0; - int toind = -1; - int xpt[2]; - int ypt[2]; - int xanchor[2]; - int yanchor[2]; - for (size_t i = 0; i < n; i++) { - VensimConnectorElement *cele = static_cast(elements[i]); - if (cele && cele->Type() == VensimViewElement::ElementTypeCONNECTOR) { - if (cele->From() == local_uid - 1) { - // check to see what to is - VensimVariableElement *stock = static_cast(elements[cele->To()]); - bool isgood = false; - if (stock) { - if ((stock->Type() == VensimViewElement::ElementTypeVARIABLE)) { - Variable *var = stock->GetVariable(); - if (var && var->VariableType() == XMILE_Type_STOCK) - isgood = true; - } else if (stock->Type() == VensimViewElement::ElementTypeCOMMENT) { - isgood = true; - } - } - if (isgood) { - xpt[count] = cele->X(); - xanchor[count] = stock->X(); - ypt[count] = cele->Y(); - yanchor[count] = stock->Y(); - if (stock->Type() == VensimViewElement::ElementTypeVARIABLE) { - Variable *var = stock->GetVariable(); - if (toind == -1 && var && var->VariableType() == XMILE_Type_STOCK) { - // are we an inflow or an outflow - for (Variable *inflow : var->Inflows()) { - if (inflow == vele->GetVariable()) { - toind = count; - break; - } - } - if (toind == -1) { - for (Variable *outflow : var->Outflows()) { - if (outflow == vele->GetVariable()) { - toind = count ? 0 : 1; - break; - } - } - } - } - } - count++; - if (count == 2) - break; - } - } - } - } - if (count < 2 || toind < 0) { - xpt[0] = vele->X() - 150; - xpt[1] = vele->X() + 25; - ypt[0] = ypt[1] = vele->Y(); - toind = 1; - } else { - if (xpt[0] == xpt[1]) { - // vertical put the ys at the achors - ypt[0] = yanchor[0]; - ypt[1] = yanchor[1]; - } else { - // horizontal put the xs at the achors - xpt[0] = xanchor[0]; - xpt[1] = xanchor[1]; - } - } - tinyxml2::XMLElement *xpts = doc->NewElement("pts"); - xvar->InsertEndChild(xpts); - tinyxml2::XMLElement *xxpt = doc->NewElement("pt"); - xpts->InsertEndChild(xxpt); - xxpt->SetAttribute("x", xpt[1 - toind]); - xxpt->SetAttribute("y", ypt[1 - toind]); - xxpt = doc->NewElement("pt"); - xpts->InsertEndChild(xxpt); - xxpt->SetAttribute("x", xpt[toind]); - xxpt->SetAttribute("y", ypt[toind]); - } - } - } else if (ele->Type() == VensimViewElement::ElementTypeCONNECTOR) { - VensimConnectorElement *cele = static_cast(ele); - if (cele->From() > 0 && cele->To() > 0) { - VensimVariableElement *from = static_cast(elements[cele->From()]); - VensimVariableElement *to = static_cast(elements[cele->To()]); - if (from && to) { - // if from is a valve we switch it to the next element in the list which should be a var - if (from->Type() == VensimViewElement::ElementTypeVALVE && - static_cast(elements[cele->From()])->Attached()) { - from = static_cast(elements[cele->From() + 1]); - } - if (to->Type() == VensimViewElement::ElementTypeVALVE && - static_cast(elements[cele->To()])->Attached()) - to = static_cast(elements[cele->To() + 1]); - if (from->Type() == VensimViewElement::ElementTypeVARIABLE && to && - to->Type() == VensimViewElement::ElementTypeVARIABLE && to->GetVariable() && - to->GetVariable()->VariableType() != XMILE_Type_STOCK) { - // valid xmile connector - tinyxml2::XMLElement *xconnector = doc->NewElement("connector"); - element->InsertEndChild(xconnector); - xconnector->SetAttribute("uid", uid); - // try to figure out the angle based on the 3 points - -#ifndef NDEBUG - double thetax = 999; - if (to->GetVariable()->GetName() == "US crude death rate") - thetax = AngleFromPoints(from->X(), from->Y(), cele->X(), cele->Y(), to->X(), to->Y()); -#endif - xconnector->SetAttribute("angle", - AngleFromPoints(from->X(), from->Y(), cele->X(), cele->Y(), to->X(), to->Y())); - if (cele->Polarity()) { - char cbuf[2]; - cbuf[0] = cele->Polarity(); - cbuf[1] = 0; - xconnector->SetAttribute("polarity", cbuf); - } - tinyxml2::XMLElement *xfrom = doc->NewElement("from"); - xconnector->InsertEndChild(xfrom); - if (from->Ghost(NULL)) { - tinyxml2::XMLElement *xalias = doc->NewElement("alias"); - xfrom->InsertEndChild(xalias); - xalias->SetAttribute("uid", view->UIDOffset() + cele->From()); - } else if (from->GetVariable()) { - xfrom->SetText(QuotedSpaceToUnderBar(from->GetVariable()->GetAlternateName()).c_str()); - } - tinyxml2::XMLElement *xto = doc->NewElement("to"); - xconnector->InsertEndChild(xto); - xto->SetText(QuotedSpaceToUnderBar(to->GetVariable()->GetAlternateName()).c_str()); - } - } - } - } - } - uid++; - local_uid++; - } -} diff --git a/src/xmutil/third_party/xmutil/Xmile/XMILEGenerator.h b/src/xmutil/third_party/xmutil/Xmile/XMILEGenerator.h deleted file mode 100644 index 95aa7d171..000000000 --- a/src/xmutil/third_party/xmutil/Xmile/XMILEGenerator.h +++ /dev/null @@ -1,45 +0,0 @@ -#pragma once -#ifndef __XMILE_H -#define __XMILE_H - -class Model; - -#include - -#include -#include -#include -class VensimView; -class SymbolNameSpace; -class Variable; - -class XMILEGenerator { -public: - XMILEGenerator(Model *model, double xtrario, double yratio, bool from_dynamo); - - std::string Print(bool is_compact, std::vector &errs, bool as_sectors); - -protected: - void generateHeader(tinyxml2::XMLElement *element, std::vector &errs); - void generateSimSpecs(tinyxml2::XMLElement *element, std::vector &errs); - void generateModelUnits(tinyxml2::XMLElement *element, std::vector &errs); - void generateDimensions(tinyxml2::XMLElement *element, std::vector &errs); - void generateModelAsSectors(tinyxml2::XMLElement *element, std::vector &errs, SymbolNameSpace *ns, - bool wantDiagram); - void generateEquation(Variable *var, tinyxml2::XMLDocument *doc, tinyxml2::XMLElement *variables); - void generateEquations(std::set &included, tinyxml2::XMLDocument *doc, tinyxml2::XMLElement *variables); - void generateModelAsModules(tinyxml2::XMLElement *element, std::vector &errs, SymbolNameSpace *ns); - bool generateModelAsGroups(tinyxml2::XMLElement *elemeGnt, std::vector &errs, SymbolNameSpace *ns); - void generateSectorViews(tinyxml2::XMLElement *views, tinyxml2::XMLElement *vars, std::vector &errs, - bool mainmodel); - void generateView(VensimView *view, tinyxml2::XMLElement *element, std::vector &errs, - std::set *needed); - -private: - Model *_model; - double _xratio; - double _yratio; - bool _from_dynamo; -}; - -#endif diff --git a/test-asan.sh b/test-asan.sh index caf7551e5..ff49de4b2 100755 --- a/test-asan.sh +++ b/test-asan.sh @@ -1,5 +1,8 @@ #!/bin/bash -set -ex +# `pipefail` is what makes this a test: the suite below is piped into `tee`, +# and without it the pipeline reports tee's status, so a failing test or an +# ASan report leaves the script -- and the scheduled job that runs it -- green. +set -exo pipefail # Test full Rust suite with ASAN on Linux echo "Running full test suite with ASAN..." @@ -28,20 +31,26 @@ export ASAN=1 TARGET=$(rustc -vV | sed -n 's/^host: //p') echo "Using target: $TARGET" -# Run tests with ASAN - testing xmutil directly and simlin-engine -# Include xmutil feature to ensure xmutil C/C++ code is exercised +# Run the engine's suite under ASan: the VM, the bytecode it runs, and the +# hashing it leans on are where the engine's `unsafe` is. # Use -Zbuild-std to rebuild std library with ASAN # Log to file for analysis -echo "Testing xmutil package..." -RUST_BACKTRACE=1 \ -ASAN_OPTIONS="detect_leaks=1:check_initialization_order=1:strict_init_order=1:verbosity=0:print_stats=1:halt_on_error=0" \ -cargo +"$NIGHTLY_TOOLCHAIN" test -Zbuild-std --target "$TARGET" -p xmutil 2>&1 | tee asan-test.log +# Every test binary runs and the summaries below print before the script +# reports a failure: `--no-fail-fast` keeps a failing unit-test binary from +# hiding the integration harness, and the status is collected rather than left +# to `set -e`, which would stop before the summaries. +# ASAN_OPTIONS deliberately leaves `halt_on_error` at its default: with +# `halt_on_error=0` a test binary that leaks prints its leak report and exits +# 0, so a leak would be a line in the log of a green job. +# `allocator_may_return_null=1` keeps the allocator's contract: a request no +# allocator can meet returns null, which `try_reserve` reports as an error the +# engine refuses a run with. ASan's default aborts the process instead. +STATUS=0 -echo "" -echo "Testing simlin-engine with xmutil feature..." +echo "Testing simlin-engine..." RUST_BACKTRACE=1 \ -ASAN_OPTIONS="detect_leaks=1:check_initialization_order=1:strict_init_order=1:verbosity=0:print_stats=1:halt_on_error=0" \ -cargo +"$NIGHTLY_TOOLCHAIN" test -Zbuild-std --target "$TARGET" -p simlin-engine --features "xmutil,file_io" 2>&1 | tee -a asan-test.log +ASAN_OPTIONS="allocator_may_return_null=1:detect_leaks=1:check_initialization_order=1:strict_init_order=1:verbosity=0:print_stats=1" \ +cargo +"$NIGHTLY_TOOLCHAIN" test -Zbuild-std --target "$TARGET" -p simlin-engine --features file_io --no-fail-fast 2>&1 | tee asan-test.log || STATUS=$? echo "" echo "=== ASAN Summary ===" @@ -52,4 +61,8 @@ echo "=== Memory Leaks Detected ===" grep "Direct leak" asan-test.log | head -10 || echo "No direct leaks found" echo "" +if [ "$STATUS" -ne 0 ]; then + echo "ASAN test FAILED (cargo exit $STATUS). Full log saved to asan-test.log" + exit "$STATUS" +fi echo "ASAN test complete! Full log saved to asan-test.log" \ No newline at end of file