oscura 0.0.1__py3-none-any.whl → 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- oscura/__init__.py +813 -8
- oscura/__main__.py +392 -0
- oscura/analyzers/__init__.py +37 -0
- oscura/analyzers/digital/__init__.py +177 -0
- oscura/analyzers/digital/bus.py +691 -0
- oscura/analyzers/digital/clock.py +805 -0
- oscura/analyzers/digital/correlation.py +720 -0
- oscura/analyzers/digital/edges.py +632 -0
- oscura/analyzers/digital/extraction.py +413 -0
- oscura/analyzers/digital/quality.py +878 -0
- oscura/analyzers/digital/signal_quality.py +877 -0
- oscura/analyzers/digital/thresholds.py +708 -0
- oscura/analyzers/digital/timing.py +1104 -0
- oscura/analyzers/eye/__init__.py +46 -0
- oscura/analyzers/eye/diagram.py +434 -0
- oscura/analyzers/eye/metrics.py +555 -0
- oscura/analyzers/jitter/__init__.py +83 -0
- oscura/analyzers/jitter/ber.py +333 -0
- oscura/analyzers/jitter/decomposition.py +759 -0
- oscura/analyzers/jitter/measurements.py +413 -0
- oscura/analyzers/jitter/spectrum.py +220 -0
- oscura/analyzers/measurements.py +40 -0
- oscura/analyzers/packet/__init__.py +171 -0
- oscura/analyzers/packet/daq.py +1077 -0
- oscura/analyzers/packet/metrics.py +437 -0
- oscura/analyzers/packet/parser.py +327 -0
- oscura/analyzers/packet/payload.py +2156 -0
- oscura/analyzers/packet/payload_analysis.py +1312 -0
- oscura/analyzers/packet/payload_extraction.py +236 -0
- oscura/analyzers/packet/payload_patterns.py +670 -0
- oscura/analyzers/packet/stream.py +359 -0
- oscura/analyzers/patterns/__init__.py +266 -0
- oscura/analyzers/patterns/clustering.py +1036 -0
- oscura/analyzers/patterns/discovery.py +539 -0
- oscura/analyzers/patterns/learning.py +797 -0
- oscura/analyzers/patterns/matching.py +1091 -0
- oscura/analyzers/patterns/periodic.py +650 -0
- oscura/analyzers/patterns/sequences.py +767 -0
- oscura/analyzers/power/__init__.py +116 -0
- oscura/analyzers/power/ac_power.py +391 -0
- oscura/analyzers/power/basic.py +383 -0
- oscura/analyzers/power/conduction.py +314 -0
- oscura/analyzers/power/efficiency.py +297 -0
- oscura/analyzers/power/ripple.py +356 -0
- oscura/analyzers/power/soa.py +372 -0
- oscura/analyzers/power/switching.py +479 -0
- oscura/analyzers/protocol/__init__.py +150 -0
- oscura/analyzers/protocols/__init__.py +150 -0
- oscura/analyzers/protocols/base.py +500 -0
- oscura/analyzers/protocols/can.py +620 -0
- oscura/analyzers/protocols/can_fd.py +448 -0
- oscura/analyzers/protocols/flexray.py +405 -0
- oscura/analyzers/protocols/hdlc.py +399 -0
- oscura/analyzers/protocols/i2c.py +368 -0
- oscura/analyzers/protocols/i2s.py +296 -0
- oscura/analyzers/protocols/jtag.py +393 -0
- oscura/analyzers/protocols/lin.py +445 -0
- oscura/analyzers/protocols/manchester.py +333 -0
- oscura/analyzers/protocols/onewire.py +501 -0
- oscura/analyzers/protocols/spi.py +334 -0
- oscura/analyzers/protocols/swd.py +325 -0
- oscura/analyzers/protocols/uart.py +393 -0
- oscura/analyzers/protocols/usb.py +495 -0
- oscura/analyzers/signal_integrity/__init__.py +63 -0
- oscura/analyzers/signal_integrity/embedding.py +294 -0
- oscura/analyzers/signal_integrity/equalization.py +370 -0
- oscura/analyzers/signal_integrity/sparams.py +484 -0
- oscura/analyzers/spectral/__init__.py +53 -0
- oscura/analyzers/spectral/chunked.py +273 -0
- oscura/analyzers/spectral/chunked_fft.py +571 -0
- oscura/analyzers/spectral/chunked_wavelet.py +391 -0
- oscura/analyzers/spectral/fft.py +92 -0
- oscura/analyzers/statistical/__init__.py +250 -0
- oscura/analyzers/statistical/checksum.py +923 -0
- oscura/analyzers/statistical/chunked_corr.py +228 -0
- oscura/analyzers/statistical/classification.py +778 -0
- oscura/analyzers/statistical/entropy.py +1113 -0
- oscura/analyzers/statistical/ngrams.py +614 -0
- oscura/analyzers/statistics/__init__.py +119 -0
- oscura/analyzers/statistics/advanced.py +885 -0
- oscura/analyzers/statistics/basic.py +263 -0
- oscura/analyzers/statistics/correlation.py +630 -0
- oscura/analyzers/statistics/distribution.py +298 -0
- oscura/analyzers/statistics/outliers.py +463 -0
- oscura/analyzers/statistics/streaming.py +93 -0
- oscura/analyzers/statistics/trend.py +520 -0
- oscura/analyzers/validation.py +598 -0
- oscura/analyzers/waveform/__init__.py +36 -0
- oscura/analyzers/waveform/measurements.py +943 -0
- oscura/analyzers/waveform/measurements_with_uncertainty.py +371 -0
- oscura/analyzers/waveform/spectral.py +1689 -0
- oscura/analyzers/waveform/wavelets.py +298 -0
- oscura/api/__init__.py +62 -0
- oscura/api/dsl.py +538 -0
- oscura/api/fluent.py +571 -0
- oscura/api/operators.py +498 -0
- oscura/api/optimization.py +392 -0
- oscura/api/profiling.py +396 -0
- oscura/automotive/__init__.py +73 -0
- oscura/automotive/can/__init__.py +52 -0
- oscura/automotive/can/analysis.py +356 -0
- oscura/automotive/can/checksum.py +250 -0
- oscura/automotive/can/correlation.py +212 -0
- oscura/automotive/can/discovery.py +355 -0
- oscura/automotive/can/message_wrapper.py +375 -0
- oscura/automotive/can/models.py +385 -0
- oscura/automotive/can/patterns.py +381 -0
- oscura/automotive/can/session.py +452 -0
- oscura/automotive/can/state_machine.py +300 -0
- oscura/automotive/can/stimulus_response.py +461 -0
- oscura/automotive/dbc/__init__.py +15 -0
- oscura/automotive/dbc/generator.py +156 -0
- oscura/automotive/dbc/parser.py +146 -0
- oscura/automotive/dtc/__init__.py +30 -0
- oscura/automotive/dtc/database.py +3036 -0
- oscura/automotive/j1939/__init__.py +14 -0
- oscura/automotive/j1939/decoder.py +745 -0
- oscura/automotive/loaders/__init__.py +35 -0
- oscura/automotive/loaders/asc.py +98 -0
- oscura/automotive/loaders/blf.py +77 -0
- oscura/automotive/loaders/csv_can.py +136 -0
- oscura/automotive/loaders/dispatcher.py +136 -0
- oscura/automotive/loaders/mdf.py +331 -0
- oscura/automotive/loaders/pcap.py +132 -0
- oscura/automotive/obd/__init__.py +14 -0
- oscura/automotive/obd/decoder.py +707 -0
- oscura/automotive/uds/__init__.py +48 -0
- oscura/automotive/uds/decoder.py +265 -0
- oscura/automotive/uds/models.py +64 -0
- oscura/automotive/visualization.py +369 -0
- oscura/batch/__init__.py +55 -0
- oscura/batch/advanced.py +627 -0
- oscura/batch/aggregate.py +300 -0
- oscura/batch/analyze.py +139 -0
- oscura/batch/logging.py +487 -0
- oscura/batch/metrics.py +556 -0
- oscura/builders/__init__.py +41 -0
- oscura/builders/signal_builder.py +1131 -0
- oscura/cli/__init__.py +14 -0
- oscura/cli/batch.py +339 -0
- oscura/cli/characterize.py +273 -0
- oscura/cli/compare.py +775 -0
- oscura/cli/decode.py +551 -0
- oscura/cli/main.py +247 -0
- oscura/cli/shell.py +350 -0
- oscura/comparison/__init__.py +66 -0
- oscura/comparison/compare.py +397 -0
- oscura/comparison/golden.py +487 -0
- oscura/comparison/limits.py +391 -0
- oscura/comparison/mask.py +434 -0
- oscura/comparison/trace_diff.py +30 -0
- oscura/comparison/visualization.py +481 -0
- oscura/compliance/__init__.py +70 -0
- oscura/compliance/advanced.py +756 -0
- oscura/compliance/masks.py +363 -0
- oscura/compliance/reporting.py +483 -0
- oscura/compliance/testing.py +298 -0
- oscura/component/__init__.py +38 -0
- oscura/component/impedance.py +365 -0
- oscura/component/reactive.py +598 -0
- oscura/component/transmission_line.py +312 -0
- oscura/config/__init__.py +191 -0
- oscura/config/defaults.py +254 -0
- oscura/config/loader.py +348 -0
- oscura/config/memory.py +271 -0
- oscura/config/migration.py +458 -0
- oscura/config/pipeline.py +1077 -0
- oscura/config/preferences.py +530 -0
- oscura/config/protocol.py +875 -0
- oscura/config/schema.py +713 -0
- oscura/config/settings.py +420 -0
- oscura/config/thresholds.py +599 -0
- oscura/convenience.py +457 -0
- oscura/core/__init__.py +299 -0
- oscura/core/audit.py +457 -0
- oscura/core/backend_selector.py +405 -0
- oscura/core/cache.py +590 -0
- oscura/core/cancellation.py +439 -0
- oscura/core/confidence.py +225 -0
- oscura/core/config.py +506 -0
- oscura/core/correlation.py +216 -0
- oscura/core/cross_domain.py +422 -0
- oscura/core/debug.py +301 -0
- oscura/core/edge_cases.py +541 -0
- oscura/core/exceptions.py +535 -0
- oscura/core/gpu_backend.py +523 -0
- oscura/core/lazy.py +832 -0
- oscura/core/log_query.py +540 -0
- oscura/core/logging.py +931 -0
- oscura/core/logging_advanced.py +952 -0
- oscura/core/memoize.py +171 -0
- oscura/core/memory_check.py +274 -0
- oscura/core/memory_guard.py +290 -0
- oscura/core/memory_limits.py +336 -0
- oscura/core/memory_monitor.py +453 -0
- oscura/core/memory_progress.py +465 -0
- oscura/core/memory_warnings.py +315 -0
- oscura/core/numba_backend.py +362 -0
- oscura/core/performance.py +352 -0
- oscura/core/progress.py +524 -0
- oscura/core/provenance.py +358 -0
- oscura/core/results.py +331 -0
- oscura/core/types.py +504 -0
- oscura/core/uncertainty.py +383 -0
- oscura/discovery/__init__.py +52 -0
- oscura/discovery/anomaly_detector.py +672 -0
- oscura/discovery/auto_decoder.py +415 -0
- oscura/discovery/comparison.py +497 -0
- oscura/discovery/quality_validator.py +528 -0
- oscura/discovery/signal_detector.py +769 -0
- oscura/dsl/__init__.py +73 -0
- oscura/dsl/commands.py +246 -0
- oscura/dsl/interpreter.py +455 -0
- oscura/dsl/parser.py +689 -0
- oscura/dsl/repl.py +172 -0
- oscura/exceptions.py +59 -0
- oscura/exploratory/__init__.py +111 -0
- oscura/exploratory/error_recovery.py +642 -0
- oscura/exploratory/fuzzy.py +513 -0
- oscura/exploratory/fuzzy_advanced.py +786 -0
- oscura/exploratory/legacy.py +831 -0
- oscura/exploratory/parse.py +358 -0
- oscura/exploratory/recovery.py +275 -0
- oscura/exploratory/sync.py +382 -0
- oscura/exploratory/unknown.py +707 -0
- oscura/export/__init__.py +25 -0
- oscura/export/wireshark/README.md +265 -0
- oscura/export/wireshark/__init__.py +47 -0
- oscura/export/wireshark/generator.py +312 -0
- oscura/export/wireshark/lua_builder.py +159 -0
- oscura/export/wireshark/templates/dissector.lua.j2 +92 -0
- oscura/export/wireshark/type_mapping.py +165 -0
- oscura/export/wireshark/validator.py +105 -0
- oscura/exporters/__init__.py +94 -0
- oscura/exporters/csv.py +303 -0
- oscura/exporters/exporters.py +44 -0
- oscura/exporters/hdf5.py +219 -0
- oscura/exporters/html_export.py +701 -0
- oscura/exporters/json_export.py +291 -0
- oscura/exporters/markdown_export.py +367 -0
- oscura/exporters/matlab_export.py +354 -0
- oscura/exporters/npz_export.py +219 -0
- oscura/exporters/spice_export.py +210 -0
- oscura/extensibility/__init__.py +131 -0
- oscura/extensibility/docs.py +752 -0
- oscura/extensibility/extensions.py +1125 -0
- oscura/extensibility/logging.py +259 -0
- oscura/extensibility/measurements.py +485 -0
- oscura/extensibility/plugins.py +414 -0
- oscura/extensibility/registry.py +346 -0
- oscura/extensibility/templates.py +913 -0
- oscura/extensibility/validation.py +651 -0
- oscura/filtering/__init__.py +89 -0
- oscura/filtering/base.py +563 -0
- oscura/filtering/convenience.py +564 -0
- oscura/filtering/design.py +725 -0
- oscura/filtering/filters.py +32 -0
- oscura/filtering/introspection.py +605 -0
- oscura/guidance/__init__.py +24 -0
- oscura/guidance/recommender.py +429 -0
- oscura/guidance/wizard.py +518 -0
- oscura/inference/__init__.py +251 -0
- oscura/inference/active_learning/README.md +153 -0
- oscura/inference/active_learning/__init__.py +38 -0
- oscura/inference/active_learning/lstar.py +257 -0
- oscura/inference/active_learning/observation_table.py +230 -0
- oscura/inference/active_learning/oracle.py +78 -0
- oscura/inference/active_learning/teachers/__init__.py +15 -0
- oscura/inference/active_learning/teachers/simulator.py +192 -0
- oscura/inference/adaptive_tuning.py +453 -0
- oscura/inference/alignment.py +653 -0
- oscura/inference/bayesian.py +943 -0
- oscura/inference/binary.py +1016 -0
- oscura/inference/crc_reverse.py +711 -0
- oscura/inference/logic.py +288 -0
- oscura/inference/message_format.py +1305 -0
- oscura/inference/protocol.py +417 -0
- oscura/inference/protocol_dsl.py +1084 -0
- oscura/inference/protocol_library.py +1230 -0
- oscura/inference/sequences.py +809 -0
- oscura/inference/signal_intelligence.py +1509 -0
- oscura/inference/spectral.py +215 -0
- oscura/inference/state_machine.py +634 -0
- oscura/inference/stream.py +918 -0
- oscura/integrations/__init__.py +59 -0
- oscura/integrations/llm.py +1827 -0
- oscura/jupyter/__init__.py +32 -0
- oscura/jupyter/display.py +268 -0
- oscura/jupyter/magic.py +334 -0
- oscura/loaders/__init__.py +526 -0
- oscura/loaders/binary.py +69 -0
- oscura/loaders/configurable.py +1255 -0
- oscura/loaders/csv.py +26 -0
- oscura/loaders/csv_loader.py +473 -0
- oscura/loaders/hdf5.py +9 -0
- oscura/loaders/hdf5_loader.py +510 -0
- oscura/loaders/lazy.py +370 -0
- oscura/loaders/mmap_loader.py +583 -0
- oscura/loaders/numpy_loader.py +436 -0
- oscura/loaders/pcap.py +432 -0
- oscura/loaders/preprocessing.py +368 -0
- oscura/loaders/rigol.py +287 -0
- oscura/loaders/sigrok.py +321 -0
- oscura/loaders/tdms.py +367 -0
- oscura/loaders/tektronix.py +711 -0
- oscura/loaders/validation.py +584 -0
- oscura/loaders/vcd.py +464 -0
- oscura/loaders/wav.py +233 -0
- oscura/math/__init__.py +45 -0
- oscura/math/arithmetic.py +824 -0
- oscura/math/interpolation.py +413 -0
- oscura/onboarding/__init__.py +39 -0
- oscura/onboarding/help.py +498 -0
- oscura/onboarding/tutorials.py +405 -0
- oscura/onboarding/wizard.py +466 -0
- oscura/optimization/__init__.py +19 -0
- oscura/optimization/parallel.py +440 -0
- oscura/optimization/search.py +532 -0
- oscura/pipeline/__init__.py +43 -0
- oscura/pipeline/base.py +338 -0
- oscura/pipeline/composition.py +242 -0
- oscura/pipeline/parallel.py +448 -0
- oscura/pipeline/pipeline.py +375 -0
- oscura/pipeline/reverse_engineering.py +1119 -0
- oscura/plugins/__init__.py +122 -0
- oscura/plugins/base.py +272 -0
- oscura/plugins/cli.py +497 -0
- oscura/plugins/discovery.py +411 -0
- oscura/plugins/isolation.py +418 -0
- oscura/plugins/lifecycle.py +959 -0
- oscura/plugins/manager.py +493 -0
- oscura/plugins/registry.py +421 -0
- oscura/plugins/versioning.py +372 -0
- oscura/py.typed +0 -0
- oscura/quality/__init__.py +65 -0
- oscura/quality/ensemble.py +740 -0
- oscura/quality/explainer.py +338 -0
- oscura/quality/scoring.py +616 -0
- oscura/quality/warnings.py +456 -0
- oscura/reporting/__init__.py +248 -0
- oscura/reporting/advanced.py +1234 -0
- oscura/reporting/analyze.py +448 -0
- oscura/reporting/argument_preparer.py +596 -0
- oscura/reporting/auto_report.py +507 -0
- oscura/reporting/batch.py +615 -0
- oscura/reporting/chart_selection.py +223 -0
- oscura/reporting/comparison.py +330 -0
- oscura/reporting/config.py +615 -0
- oscura/reporting/content/__init__.py +39 -0
- oscura/reporting/content/executive.py +127 -0
- oscura/reporting/content/filtering.py +191 -0
- oscura/reporting/content/minimal.py +257 -0
- oscura/reporting/content/verbosity.py +162 -0
- oscura/reporting/core.py +508 -0
- oscura/reporting/core_formats/__init__.py +17 -0
- oscura/reporting/core_formats/multi_format.py +210 -0
- oscura/reporting/engine.py +836 -0
- oscura/reporting/export.py +366 -0
- oscura/reporting/formatting/__init__.py +129 -0
- oscura/reporting/formatting/emphasis.py +81 -0
- oscura/reporting/formatting/numbers.py +403 -0
- oscura/reporting/formatting/standards.py +55 -0
- oscura/reporting/formatting.py +466 -0
- oscura/reporting/html.py +578 -0
- oscura/reporting/index.py +590 -0
- oscura/reporting/multichannel.py +296 -0
- oscura/reporting/output.py +379 -0
- oscura/reporting/pdf.py +373 -0
- oscura/reporting/plots.py +731 -0
- oscura/reporting/pptx_export.py +360 -0
- oscura/reporting/renderers/__init__.py +11 -0
- oscura/reporting/renderers/pdf.py +94 -0
- oscura/reporting/sections.py +471 -0
- oscura/reporting/standards.py +680 -0
- oscura/reporting/summary_generator.py +368 -0
- oscura/reporting/tables.py +397 -0
- oscura/reporting/template_system.py +724 -0
- oscura/reporting/templates/__init__.py +15 -0
- oscura/reporting/templates/definition.py +205 -0
- oscura/reporting/templates/index.html +649 -0
- oscura/reporting/templates/index.md +173 -0
- oscura/schemas/__init__.py +158 -0
- oscura/schemas/bus_configuration.json +322 -0
- oscura/schemas/device_mapping.json +182 -0
- oscura/schemas/packet_format.json +418 -0
- oscura/schemas/protocol_definition.json +363 -0
- oscura/search/__init__.py +16 -0
- oscura/search/anomaly.py +292 -0
- oscura/search/context.py +149 -0
- oscura/search/pattern.py +160 -0
- oscura/session/__init__.py +34 -0
- oscura/session/annotations.py +289 -0
- oscura/session/history.py +313 -0
- oscura/session/session.py +445 -0
- oscura/streaming/__init__.py +43 -0
- oscura/streaming/chunked.py +611 -0
- oscura/streaming/progressive.py +393 -0
- oscura/streaming/realtime.py +622 -0
- oscura/testing/__init__.py +54 -0
- oscura/testing/synthetic.py +808 -0
- oscura/triggering/__init__.py +68 -0
- oscura/triggering/base.py +229 -0
- oscura/triggering/edge.py +353 -0
- oscura/triggering/pattern.py +344 -0
- oscura/triggering/pulse.py +581 -0
- oscura/triggering/window.py +453 -0
- oscura/ui/__init__.py +48 -0
- oscura/ui/formatters.py +526 -0
- oscura/ui/progressive_display.py +340 -0
- oscura/utils/__init__.py +99 -0
- oscura/utils/autodetect.py +338 -0
- oscura/utils/buffer.py +389 -0
- oscura/utils/lazy.py +407 -0
- oscura/utils/lazy_imports.py +147 -0
- oscura/utils/memory.py +836 -0
- oscura/utils/memory_advanced.py +1326 -0
- oscura/utils/memory_extensions.py +465 -0
- oscura/utils/progressive.py +352 -0
- oscura/utils/windowing.py +362 -0
- oscura/visualization/__init__.py +321 -0
- oscura/visualization/accessibility.py +526 -0
- oscura/visualization/annotations.py +374 -0
- oscura/visualization/axis_scaling.py +305 -0
- oscura/visualization/colors.py +453 -0
- oscura/visualization/digital.py +337 -0
- oscura/visualization/eye.py +420 -0
- oscura/visualization/histogram.py +281 -0
- oscura/visualization/interactive.py +858 -0
- oscura/visualization/jitter.py +702 -0
- oscura/visualization/keyboard.py +394 -0
- oscura/visualization/layout.py +365 -0
- oscura/visualization/optimization.py +1028 -0
- oscura/visualization/palettes.py +446 -0
- oscura/visualization/plot.py +92 -0
- oscura/visualization/power.py +290 -0
- oscura/visualization/power_extended.py +626 -0
- oscura/visualization/presets.py +467 -0
- oscura/visualization/protocols.py +932 -0
- oscura/visualization/render.py +207 -0
- oscura/visualization/rendering.py +444 -0
- oscura/visualization/reverse_engineering.py +791 -0
- oscura/visualization/signal_integrity.py +808 -0
- oscura/visualization/specialized.py +553 -0
- oscura/visualization/spectral.py +811 -0
- oscura/visualization/styles.py +381 -0
- oscura/visualization/thumbnails.py +311 -0
- oscura/visualization/time_axis.py +351 -0
- oscura/visualization/waveform.py +367 -0
- oscura/workflow/__init__.py +13 -0
- oscura/workflow/dag.py +377 -0
- oscura/workflows/__init__.py +58 -0
- oscura/workflows/compliance.py +280 -0
- oscura/workflows/digital.py +272 -0
- oscura/workflows/multi_trace.py +502 -0
- oscura/workflows/power.py +178 -0
- oscura/workflows/protocol.py +492 -0
- oscura/workflows/reverse_engineering.py +639 -0
- oscura/workflows/signal_integrity.py +227 -0
- oscura-0.1.0.dist-info/METADATA +300 -0
- oscura-0.1.0.dist-info/RECORD +463 -0
- oscura-0.1.0.dist-info/entry_points.txt +2 -0
- {oscura-0.0.1.dist-info → oscura-0.1.0.dist-info}/licenses/LICENSE +1 -1
- oscura-0.0.1.dist-info/METADATA +0 -63
- oscura-0.0.1.dist-info/RECORD +0 -5
- {oscura-0.0.1.dist-info → oscura-0.1.0.dist-info}/WHEEL +0 -0
|
@@ -0,0 +1,455 @@
|
|
|
1
|
+
"""TraceKit DSL Interpreter.
|
|
2
|
+
|
|
3
|
+
Executes parsed DSL programs.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from collections.abc import Callable
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
from typing import Any
|
|
9
|
+
|
|
10
|
+
from oscura.dsl.parser import (
|
|
11
|
+
Assignment,
|
|
12
|
+
Command,
|
|
13
|
+
Expression,
|
|
14
|
+
ForLoop,
|
|
15
|
+
FunctionCall,
|
|
16
|
+
Literal,
|
|
17
|
+
Pipeline,
|
|
18
|
+
Statement,
|
|
19
|
+
Variable,
|
|
20
|
+
parse_dsl,
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class InterpreterError(Exception):
|
|
25
|
+
"""DSL interpreter error.
|
|
26
|
+
|
|
27
|
+
Raised when the interpreter encounters an error during execution,
|
|
28
|
+
such as undefined variables, unknown commands, or invalid operations.
|
|
29
|
+
"""
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class Interpreter:
|
|
33
|
+
"""DSL interpreter for TraceKit commands.
|
|
34
|
+
|
|
35
|
+
Executes parsed AST with Python implementations of DSL commands.
|
|
36
|
+
"""
|
|
37
|
+
|
|
38
|
+
def __init__(self) -> None:
|
|
39
|
+
"""Initialize interpreter with empty environment."""
|
|
40
|
+
self.variables: dict[str, Any] = {}
|
|
41
|
+
self.commands: dict[str, Any] = {}
|
|
42
|
+
self._register_builtin_commands()
|
|
43
|
+
|
|
44
|
+
def _register_builtin_commands(self) -> None:
|
|
45
|
+
"""Register built-in DSL commands."""
|
|
46
|
+
# Import oscura functions lazily to avoid circular imports
|
|
47
|
+
self.commands["load"] = self._cmd_load
|
|
48
|
+
self.commands["filter"] = self._cmd_filter
|
|
49
|
+
self.commands["measure"] = self._cmd_measure
|
|
50
|
+
self.commands["plot"] = self._cmd_plot
|
|
51
|
+
self.commands["export"] = self._cmd_export
|
|
52
|
+
self.commands["glob"] = self._cmd_glob
|
|
53
|
+
|
|
54
|
+
def _cmd_load(self, *args: Any) -> Any:
|
|
55
|
+
"""Load command: load "filename"."""
|
|
56
|
+
if len(args) != 1:
|
|
57
|
+
raise InterpreterError("load requires exactly 1 argument: filename")
|
|
58
|
+
|
|
59
|
+
filename = args[0]
|
|
60
|
+
if not isinstance(filename, str):
|
|
61
|
+
raise InterpreterError("load filename must be a string")
|
|
62
|
+
|
|
63
|
+
# Lazy import to avoid circular dependency
|
|
64
|
+
try:
|
|
65
|
+
from oscura.loaders import load
|
|
66
|
+
|
|
67
|
+
# Try to determine loader from extension
|
|
68
|
+
path = Path(filename)
|
|
69
|
+
if not path.exists():
|
|
70
|
+
raise InterpreterError(f"File not found: {filename}")
|
|
71
|
+
|
|
72
|
+
# Load the file using TraceKit's loader
|
|
73
|
+
return load(str(path))
|
|
74
|
+
|
|
75
|
+
except ImportError as e:
|
|
76
|
+
raise InterpreterError(f"oscura.loaders not available: {e}") # noqa: B904
|
|
77
|
+
except Exception as e:
|
|
78
|
+
raise InterpreterError(f"Failed to load {filename}: {e}") # noqa: B904
|
|
79
|
+
|
|
80
|
+
def _cmd_filter(self, trace: Any, *args: Any) -> Any:
|
|
81
|
+
"""Filter command: filter lowpass 1000."""
|
|
82
|
+
if len(args) < 1:
|
|
83
|
+
raise InterpreterError("filter requires filter type argument")
|
|
84
|
+
|
|
85
|
+
filter_type = args[0]
|
|
86
|
+
if not isinstance(filter_type, str):
|
|
87
|
+
raise InterpreterError("filter type must be a string")
|
|
88
|
+
|
|
89
|
+
# Import filter functions
|
|
90
|
+
try:
|
|
91
|
+
from oscura import filtering
|
|
92
|
+
|
|
93
|
+
# Note: bandpass and bandstop have different signatures (low, high)
|
|
94
|
+
# but we treat them as Any -> Any for DSL simplicity
|
|
95
|
+
filter_map: dict[str, Callable[..., Any]] = {
|
|
96
|
+
"lowpass": filtering.low_pass,
|
|
97
|
+
"highpass": filtering.high_pass,
|
|
98
|
+
"bandpass": filtering.band_pass,
|
|
99
|
+
"bandstop": filtering.band_stop,
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
if filter_type not in filter_map:
|
|
103
|
+
raise InterpreterError(
|
|
104
|
+
f"Unknown filter type: {filter_type}. Available: {', '.join(filter_map.keys())}"
|
|
105
|
+
)
|
|
106
|
+
|
|
107
|
+
# Get cutoff frequency (required for all filters)
|
|
108
|
+
if len(args) < 2:
|
|
109
|
+
raise InterpreterError(f"{filter_type} filter requires cutoff frequency")
|
|
110
|
+
|
|
111
|
+
cutoff = args[1]
|
|
112
|
+
if not isinstance(cutoff, (int, float)):
|
|
113
|
+
raise InterpreterError("cutoff frequency must be a number")
|
|
114
|
+
|
|
115
|
+
# Apply filter
|
|
116
|
+
filter_func: Callable[..., Any] = filter_map[filter_type]
|
|
117
|
+
return filter_func(trace, cutoff)
|
|
118
|
+
|
|
119
|
+
except ImportError as e:
|
|
120
|
+
raise InterpreterError(f"oscura.filtering not available: {e}") # noqa: B904
|
|
121
|
+
except Exception as e:
|
|
122
|
+
raise InterpreterError(f"Filter failed: {e}") # noqa: B904
|
|
123
|
+
|
|
124
|
+
def _cmd_measure(self, trace: Any, *args: Any) -> Any:
|
|
125
|
+
"""Measure command: measure rise_time."""
|
|
126
|
+
if len(args) < 1:
|
|
127
|
+
raise InterpreterError("measure requires measurement name")
|
|
128
|
+
|
|
129
|
+
measurement = args[0]
|
|
130
|
+
if not isinstance(measurement, str):
|
|
131
|
+
raise InterpreterError("measurement name must be a string")
|
|
132
|
+
|
|
133
|
+
# Import measurement functions
|
|
134
|
+
try:
|
|
135
|
+
import oscura
|
|
136
|
+
|
|
137
|
+
# Map measurement names to functions - access via getattr
|
|
138
|
+
measure_map: dict[str, str] = {
|
|
139
|
+
"frequency": "frequency",
|
|
140
|
+
"period": "period",
|
|
141
|
+
"duty_cycle": "duty_cycle",
|
|
142
|
+
"rise_time": "rise_time",
|
|
143
|
+
"fall_time": "fall_time",
|
|
144
|
+
"amplitude": "amplitude",
|
|
145
|
+
"rms": "rms",
|
|
146
|
+
"mean": "mean",
|
|
147
|
+
"peak_to_peak": "vpp",
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
if measurement not in measure_map:
|
|
151
|
+
raise InterpreterError(
|
|
152
|
+
f"Unknown measurement: {measurement}. "
|
|
153
|
+
f"Available: {', '.join(measure_map.keys())}"
|
|
154
|
+
)
|
|
155
|
+
|
|
156
|
+
# Apply measurement - use getattr to access the function
|
|
157
|
+
func_name = measure_map[measurement]
|
|
158
|
+
measure_func = getattr(oscura, func_name)
|
|
159
|
+
return measure_func(trace)
|
|
160
|
+
|
|
161
|
+
except ImportError as e:
|
|
162
|
+
raise InterpreterError(f"oscura measurements not available: {e}") # noqa: B904
|
|
163
|
+
except AttributeError as e:
|
|
164
|
+
raise InterpreterError(f"Measurement function not found: {e}") # noqa: B904
|
|
165
|
+
except Exception as e:
|
|
166
|
+
raise InterpreterError(f"Measurement failed: {e}") # noqa: B904
|
|
167
|
+
|
|
168
|
+
def _cmd_plot(self, trace: Any, *args: Any) -> Any:
|
|
169
|
+
"""Plot command: plot."""
|
|
170
|
+
# Import visualization functions
|
|
171
|
+
try:
|
|
172
|
+
from oscura import visualization
|
|
173
|
+
|
|
174
|
+
# Optional plot type (default: waveform)
|
|
175
|
+
plot_type = "waveform"
|
|
176
|
+
if len(args) > 0:
|
|
177
|
+
plot_type = args[0]
|
|
178
|
+
if not isinstance(plot_type, str):
|
|
179
|
+
raise InterpreterError("plot type must be a string")
|
|
180
|
+
|
|
181
|
+
# Plot based on type - use getattr to access the plot function
|
|
182
|
+
if plot_type == "waveform":
|
|
183
|
+
plot_func = getattr(visualization, "plot", None)
|
|
184
|
+
if plot_func is None:
|
|
185
|
+
raise InterpreterError("visualization.plot not available")
|
|
186
|
+
plot_func(trace)
|
|
187
|
+
return trace # Return trace for chaining
|
|
188
|
+
else:
|
|
189
|
+
raise InterpreterError(f"Unknown plot type: {plot_type}. Available: waveform")
|
|
190
|
+
|
|
191
|
+
except ImportError as e:
|
|
192
|
+
raise InterpreterError(f"oscura.visualization not available: {e}") # noqa: B904
|
|
193
|
+
except Exception as e:
|
|
194
|
+
raise InterpreterError(f"Plot failed: {e}") # noqa: B904
|
|
195
|
+
|
|
196
|
+
def _cmd_export(self, data: Any, *args: Any) -> Any:
|
|
197
|
+
"""Export command: export json "output.json"."""
|
|
198
|
+
if len(args) < 1:
|
|
199
|
+
raise InterpreterError("export requires format argument")
|
|
200
|
+
|
|
201
|
+
format_type = args[0]
|
|
202
|
+
if not isinstance(format_type, str):
|
|
203
|
+
raise InterpreterError("export format must be a string")
|
|
204
|
+
|
|
205
|
+
# Get optional filename
|
|
206
|
+
filename = None
|
|
207
|
+
if len(args) > 1:
|
|
208
|
+
filename = args[1]
|
|
209
|
+
if not isinstance(filename, str):
|
|
210
|
+
raise InterpreterError("export filename must be a string")
|
|
211
|
+
|
|
212
|
+
# Import export functions
|
|
213
|
+
try:
|
|
214
|
+
from oscura.exporters import csv as csv_exporter
|
|
215
|
+
from oscura.exporters import json_export
|
|
216
|
+
|
|
217
|
+
export_map: dict[str, Any] = {
|
|
218
|
+
"csv": csv_exporter.export_csv,
|
|
219
|
+
"json": json_export.export_json,
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
if format_type not in export_map:
|
|
223
|
+
raise InterpreterError(
|
|
224
|
+
f"Unknown export format: {format_type}. "
|
|
225
|
+
f"Available: {', '.join(export_map.keys())}"
|
|
226
|
+
)
|
|
227
|
+
|
|
228
|
+
# Export data
|
|
229
|
+
export_func = export_map[format_type]
|
|
230
|
+
if filename:
|
|
231
|
+
export_func(data, filename)
|
|
232
|
+
return filename
|
|
233
|
+
else:
|
|
234
|
+
# Generate default filename
|
|
235
|
+
default_name = f"export.{format_type}"
|
|
236
|
+
export_func(data, default_name)
|
|
237
|
+
return default_name
|
|
238
|
+
|
|
239
|
+
except ImportError as e:
|
|
240
|
+
raise InterpreterError(f"oscura.export not available: {e}") # noqa: B904
|
|
241
|
+
except Exception as e:
|
|
242
|
+
raise InterpreterError(f"Export failed: {e}") # noqa: B904
|
|
243
|
+
|
|
244
|
+
def _cmd_glob(self, pattern: str) -> list[str]:
|
|
245
|
+
"""Glob command: glob("*.csv")."""
|
|
246
|
+
if not isinstance(pattern, str):
|
|
247
|
+
raise InterpreterError("glob pattern must be a string")
|
|
248
|
+
|
|
249
|
+
from glob import glob as glob_func
|
|
250
|
+
|
|
251
|
+
return list(glob_func(pattern)) # noqa: PTH207
|
|
252
|
+
|
|
253
|
+
def eval_expression(self, expr: Expression) -> Any:
|
|
254
|
+
"""Evaluate an expression.
|
|
255
|
+
|
|
256
|
+
Args:
|
|
257
|
+
expr: Expression AST node
|
|
258
|
+
|
|
259
|
+
Returns:
|
|
260
|
+
Evaluated result
|
|
261
|
+
|
|
262
|
+
Raises:
|
|
263
|
+
InterpreterError: On evaluation errors
|
|
264
|
+
"""
|
|
265
|
+
# Literal value
|
|
266
|
+
if isinstance(expr, Literal):
|
|
267
|
+
return expr.value
|
|
268
|
+
|
|
269
|
+
# Variable reference
|
|
270
|
+
if isinstance(expr, Variable):
|
|
271
|
+
if expr.name not in self.variables:
|
|
272
|
+
raise InterpreterError(f"Undefined variable: {expr.name} at line {expr.line}")
|
|
273
|
+
return self.variables[expr.name]
|
|
274
|
+
|
|
275
|
+
# Function call
|
|
276
|
+
if isinstance(expr, FunctionCall):
|
|
277
|
+
return self.eval_function_call(expr)
|
|
278
|
+
|
|
279
|
+
# Command
|
|
280
|
+
if isinstance(expr, Command):
|
|
281
|
+
return self.eval_command(expr, None)
|
|
282
|
+
|
|
283
|
+
# Pipeline
|
|
284
|
+
if isinstance(expr, Pipeline):
|
|
285
|
+
return self.eval_pipeline(expr)
|
|
286
|
+
|
|
287
|
+
raise InterpreterError(f"Unknown expression type: {type(expr).__name__}")
|
|
288
|
+
|
|
289
|
+
def eval_function_call(self, func: FunctionCall) -> Any:
|
|
290
|
+
"""Evaluate function call."""
|
|
291
|
+
if func.name not in self.commands:
|
|
292
|
+
raise InterpreterError(f"Unknown function: {func.name} at line {func.line}")
|
|
293
|
+
|
|
294
|
+
# Evaluate arguments
|
|
295
|
+
args = [self.eval_expression(arg) for arg in func.args]
|
|
296
|
+
|
|
297
|
+
# Call command function
|
|
298
|
+
return self.commands[func.name](*args)
|
|
299
|
+
|
|
300
|
+
def eval_command(self, cmd: Command, input_data: Any | None) -> Any:
|
|
301
|
+
"""Evaluate command with optional piped input.
|
|
302
|
+
|
|
303
|
+
Args:
|
|
304
|
+
cmd: Command AST node
|
|
305
|
+
input_data: Input from previous pipeline stage (or None)
|
|
306
|
+
|
|
307
|
+
Returns:
|
|
308
|
+
Command result
|
|
309
|
+
|
|
310
|
+
Raises:
|
|
311
|
+
InterpreterError: If command is unknown
|
|
312
|
+
"""
|
|
313
|
+
if cmd.name not in self.commands:
|
|
314
|
+
raise InterpreterError(f"Unknown command: {cmd.name} at line {cmd.line}")
|
|
315
|
+
|
|
316
|
+
# Evaluate arguments
|
|
317
|
+
args = [self.eval_expression(arg) for arg in cmd.args]
|
|
318
|
+
|
|
319
|
+
# If there's input data, prepend it as first argument
|
|
320
|
+
if input_data is not None:
|
|
321
|
+
args = [input_data, *args]
|
|
322
|
+
|
|
323
|
+
# Call command function
|
|
324
|
+
return self.commands[cmd.name](*args)
|
|
325
|
+
|
|
326
|
+
def eval_pipeline(self, pipeline: Pipeline) -> Any:
|
|
327
|
+
"""Evaluate pipeline of commands.
|
|
328
|
+
|
|
329
|
+
Args:
|
|
330
|
+
pipeline: Pipeline AST node
|
|
331
|
+
|
|
332
|
+
Returns:
|
|
333
|
+
Final pipeline result
|
|
334
|
+
|
|
335
|
+
Raises:
|
|
336
|
+
InterpreterError: If pipeline stage is invalid
|
|
337
|
+
"""
|
|
338
|
+
result = None
|
|
339
|
+
|
|
340
|
+
for i, stage in enumerate(pipeline.stages):
|
|
341
|
+
if i == 0:
|
|
342
|
+
# First stage - no input
|
|
343
|
+
if isinstance(stage, Command):
|
|
344
|
+
result = self.eval_command(stage, None)
|
|
345
|
+
else:
|
|
346
|
+
result = self.eval_expression(stage)
|
|
347
|
+
# Subsequent stages - pipe input from previous
|
|
348
|
+
elif isinstance(stage, Command):
|
|
349
|
+
result = self.eval_command(stage, result)
|
|
350
|
+
else:
|
|
351
|
+
raise InterpreterError(
|
|
352
|
+
f"Pipeline stage {i + 1} must be a command, got {type(stage).__name__}"
|
|
353
|
+
)
|
|
354
|
+
|
|
355
|
+
return result
|
|
356
|
+
|
|
357
|
+
def eval_statement(self, stmt: Statement) -> None:
|
|
358
|
+
"""Execute a statement.
|
|
359
|
+
|
|
360
|
+
Args:
|
|
361
|
+
stmt: Statement AST node
|
|
362
|
+
|
|
363
|
+
Raises:
|
|
364
|
+
InterpreterError: On execution errors
|
|
365
|
+
"""
|
|
366
|
+
# Assignment
|
|
367
|
+
if isinstance(stmt, Assignment):
|
|
368
|
+
value = self.eval_expression(stmt.expression)
|
|
369
|
+
self.variables[stmt.variable] = value
|
|
370
|
+
return
|
|
371
|
+
|
|
372
|
+
# For loop
|
|
373
|
+
if isinstance(stmt, ForLoop):
|
|
374
|
+
self.eval_for_loop(stmt)
|
|
375
|
+
return
|
|
376
|
+
|
|
377
|
+
# Expression statement (pipeline)
|
|
378
|
+
if isinstance(stmt, Pipeline):
|
|
379
|
+
self.eval_pipeline(stmt)
|
|
380
|
+
return
|
|
381
|
+
|
|
382
|
+
# Function call as statement (e.g., in for loop body: process($f))
|
|
383
|
+
if isinstance(stmt, FunctionCall):
|
|
384
|
+
self.eval_function_call(stmt)
|
|
385
|
+
return
|
|
386
|
+
|
|
387
|
+
# All Statement types covered above (Assignment, ForLoop, Pipeline, FunctionCall)
|
|
388
|
+
# This line is unreachable if type system is correct, but kept for runtime safety
|
|
389
|
+
raise InterpreterError(f"Unknown statement type: {type(stmt).__name__}") # type: ignore[unreachable]
|
|
390
|
+
|
|
391
|
+
def eval_for_loop(self, loop: ForLoop) -> None:
|
|
392
|
+
"""Execute for loop.
|
|
393
|
+
|
|
394
|
+
Args:
|
|
395
|
+
loop: ForLoop AST node
|
|
396
|
+
|
|
397
|
+
Raises:
|
|
398
|
+
InterpreterError: If iterable is not iterable
|
|
399
|
+
"""
|
|
400
|
+
# Evaluate iterable
|
|
401
|
+
iterable = self.eval_expression(loop.iterable)
|
|
402
|
+
|
|
403
|
+
if not hasattr(iterable, "__iter__"):
|
|
404
|
+
raise InterpreterError(
|
|
405
|
+
f"For loop iterable is not iterable: {type(iterable).__name__} at line {loop.line}"
|
|
406
|
+
)
|
|
407
|
+
|
|
408
|
+
# Execute body for each item
|
|
409
|
+
for item in iterable:
|
|
410
|
+
# Set loop variable
|
|
411
|
+
self.variables[loop.variable] = item
|
|
412
|
+
|
|
413
|
+
# Execute body statements
|
|
414
|
+
for stmt in loop.body:
|
|
415
|
+
self.eval_statement(stmt)
|
|
416
|
+
|
|
417
|
+
def execute(self, statements: list[Statement]) -> None:
|
|
418
|
+
"""Execute a program (list of statements).
|
|
419
|
+
|
|
420
|
+
Args:
|
|
421
|
+
statements: AST (list of statements)
|
|
422
|
+
"""
|
|
423
|
+
for stmt in statements:
|
|
424
|
+
self.eval_statement(stmt)
|
|
425
|
+
|
|
426
|
+
def execute_source(self, source: str) -> None:
|
|
427
|
+
"""Parse and execute DSL source code.
|
|
428
|
+
|
|
429
|
+
Args:
|
|
430
|
+
source: DSL source code
|
|
431
|
+
"""
|
|
432
|
+
ast = parse_dsl(source)
|
|
433
|
+
self.execute(ast)
|
|
434
|
+
|
|
435
|
+
|
|
436
|
+
def execute_dsl(source: str, variables: dict[str, Any] | None = None) -> dict[str, Any]:
|
|
437
|
+
"""Execute TraceKit DSL source code.
|
|
438
|
+
|
|
439
|
+
Args:
|
|
440
|
+
source: DSL source code
|
|
441
|
+
variables: Optional initial variables
|
|
442
|
+
|
|
443
|
+
Returns:
|
|
444
|
+
Final variable environment after execution
|
|
445
|
+
"""
|
|
446
|
+
interpreter = Interpreter()
|
|
447
|
+
|
|
448
|
+
# Set initial variables
|
|
449
|
+
if variables:
|
|
450
|
+
interpreter.variables.update(variables)
|
|
451
|
+
|
|
452
|
+
# Execute
|
|
453
|
+
interpreter.execute_source(source)
|
|
454
|
+
|
|
455
|
+
return interpreter.variables
|