You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@skywalking.apache.org by wu...@apache.org on 2026/03/03 12:55:53 UTC

(skywalking) 08/27: Merge grammar and compiler modules into analyzer modules; wire MAL DSL to on-the-fly compilation

This is an automated email from the ASF dual-hosted git repository.

wusheng pushed a commit to branch groovy-replace
in repository https://gitbox.apache.org/repos/asf/skywalking.git

commit b5b4ee9123d92e2dfba04f30681b2f4867a69fe2
Author: Wu Sheng <wu...@foxmail.com>
AuthorDate: Sun Mar 1 01:53:14 2026 +0800

    Merge grammar and compiler modules into analyzer modules; wire MAL DSL to on-the-fly compilation
    
    - Merge mal-grammar + mal-compiler into meter-analyzer
    - Merge lal-grammar + lal-compiler into log-analyzer
    - Merge hierarchy-rule-grammar + hierarchy-rule-compiler into hierarchy
    - Remove 6 standalone modules (3 grammar + 3 compiler)
    - Update DSL.java to compile MAL expressions on-the-fly via MALClassGenerator
      instead of loading from non-existent manifest file
    - Add varargs handling for tagEqual/tagNotEqual/tagMatch/tagNotMatch in
      generated Javassist code (wrap String args in new String[]{})
    - Update test/script-compiler checker POMs to reference merged module names
    - Update CLAUDE.md files with merged file structure and paths
    
    Co-Authored-By: Claude Opus 4.6 <no...@anthropic.com>
---
 docs/en/academy/groovy-replacement-plan.md         |  117 ++-
 .../core/config/JavaHierarchyRuleProvider.java     |   86 --
 .../core/config/JavaHierarchyRuleProviderTest.java |  155 ---
 oap-server/analyzer/hierarchy/CLAUDE.md            |  113 ++
 .../{hierarchy-v1-v2-checker => hierarchy}/pom.xml |   38 +-
 .../hierarchy/rt/grammar/HierarchyRuleLexer.g4     |  105 ++
 .../hierarchy/rt/grammar/HierarchyRuleParser.g4    |  135 +++
 .../compiler/HierarchyRuleClassGenerator.java      |  294 ++++++
 .../core/config/compiler/HierarchyRuleModel.java   |  260 +++++
 .../config/compiler/HierarchyRuleScriptParser.java |  375 +++++++
 .../compiler/rt/HierarchyRulePackageHolder.java}   |   14 +-
 .../compiler/HierarchyRuleClassGeneratorTest.java  |  122 +++
 .../compiler/HierarchyRuleScriptParserTest.java    |  159 +++
 .../server/transpiler/lal/LalToJavaTranspiler.java |  923 ----------------
 .../transpiler/lal/LalToJavaTranspilerTest.java    |  678 ------------
 oap-server/analyzer/log-analyzer-v2/pom.xml        |   72 --
 .../skywalking/oap/log/analyzer/dsl/DSL.java       |  142 ---
 oap-server/analyzer/log-analyzer/CLAUDE.md         |  128 +++
 oap-server/analyzer/log-analyzer/pom.xml           |   33 +-
 .../apache/skywalking/lal/rt/grammar/LALLexer.g4   |  175 ++++
 .../apache/skywalking/lal/rt/grammar/LALParser.g4  |  464 +++++++++
 .../log/analyzer/compiler/LALClassGenerator.java   |  627 +++++++++++
 .../oap/log/analyzer/compiler/LALScriptModel.java  |  459 ++++++++
 .../oap/log/analyzer/compiler/LALScriptParser.java |  687 ++++++++++++
 .../log/analyzer/compiler/rt/BindingAware.java}    |   17 +-
 .../compiler/rt/LalExpressionPackageHolder.java}   |   14 +-
 .../skywalking/oap/log/analyzer/dsl/Binding.java   |   84 +-
 .../skywalking/oap/log/analyzer/dsl/DSL.java       |  161 +--
 .../oap/log/analyzer/dsl/spec/AbstractSpec.java    |    9 +-
 .../analyzer/dsl/spec/extractor/ExtractorSpec.java |   95 --
 .../log/analyzer/dsl/spec/filter/FilterSpec.java   |  106 --
 .../log/analyzer/dsl/spec/sink/SamplerSpec.java    |   34 -
 .../oap/log/analyzer/dsl/spec/sink/SinkSpec.java   |   27 -
 .../analyzer/compiler/LALClassGeneratorTest.java   |   97 ++
 .../log/analyzer/compiler/LALScriptParserTest.java |  192 ++++
 .../skywalking/oap/log/analyzer/dsl/DSLV2Test.java |    0
 .../server/transpiler/mal/MalToJavaTranspiler.java | 1099 --------------------
 .../transpiler/mal/MalToJavaTranspilerTest.java    |  904 ----------------
 oap-server/analyzer/meter-analyzer-v2/pom.xml      |   78 --
 .../skywalking/oap/meter/analyzer/dsl/DSL.java     |  116 ---
 .../oap/meter/analyzer/dsl/Expression.java         |   92 --
 .../oap/meter/analyzer/dsl/FilterExpression.java   |  113 --
 oap-server/analyzer/meter-analyzer/CLAUDE.md       |  100 ++
 oap-server/analyzer/meter-analyzer/pom.xml         |   32 +-
 .../apache/skywalking/mal/rt/grammar/MALLexer.g4   |  111 ++
 .../apache/skywalking/mal/rt/grammar/MALParser.g4  |  232 +++++
 .../skywalking/oap/meter/analyzer/Analyzer.java    |    6 +-
 .../meter/analyzer/compiler/MALClassGenerator.java |  971 +++++++++++++++++
 .../analyzer/compiler/MALExpressionModel.java      |  480 +++++++++
 .../meter/analyzer/compiler/MALScriptParser.java   |  491 +++++++++
 .../rt/MalExpressionPackageHolder.java}            |   14 +-
 .../skywalking/oap/meter/analyzer/dsl/DSL.java     |   72 +-
 .../oap/meter/analyzer/dsl/Expression.java         |  113 +-
 .../oap/meter/analyzer/dsl/ExpressionMetadata.java |   66 ++
 .../oap/meter/analyzer/dsl/FilterExpression.java   |   73 +-
 .../oap/meter/analyzer/dsl/MalExpression.java      |    9 +-
 .../oap/meter/analyzer/dsl/SampleFamily.java       |  158 +--
 .../analyzer/compiler/MALClassGeneratorTest.java   |  115 ++
 .../analyzer/compiler/MALScriptParserTest.java     |  238 +++++
 .../oap/meter/analyzer/dsl/DSLV2Test.java          |   42 +-
 oap-server/analyzer/pom.xml                        |   13 +-
 .../oap/query/graphql/resolver/PprofQuery.java     |    2 +-
 pom.xml                                            |    1 +
 .../hierarchy-v1-v2-checker/pom.xml                |   10 +-
 .../core/config/HierarchyRuleComparisonTest.java   |    0
 .../src/test/resources/hierarchy-definition.yml    |    0
 .../hierarchy-v1-with-groovy}/pom.xml              |    4 +-
 .../core/config/GroovyHierarchyRuleProvider.java   |    0
 .../script-compiler/lal-v1-with-groovy}/pom.xml    |   13 +-
 .../skywalking/oap/log/analyzer/dsl/Binding.java   |    0
 .../skywalking/oap/log/analyzer/dsl/DSL.java       |    0
 .../log/analyzer/dsl/LALPrecompiledExtension.java  |    0
 .../oap/log/analyzer/dsl/LalExpression.java        |   12 +-
 .../oap/log/analyzer/dsl/spec/AbstractSpec.java    |    0
 .../log/analyzer/dsl/spec/LALDelegatingScript.java |    0
 .../analyzer/dsl/spec/extractor/ExtractorSpec.java |    0
 .../extractor/sampledtrace/SampledTraceSpec.java   |  105 ++
 .../dsl/spec/extractor/slowsql/SlowSqlSpec.java    |   57 +-
 .../log/analyzer/dsl/spec/filter/FilterSpec.java   |    0
 .../dsl/spec/parser/AbstractParserSpec.java        |   49 +
 .../analyzer/dsl/spec/parser/JsonParserSpec.java   |   40 +
 .../analyzer/dsl/spec/parser/TextParserSpec.java   |   57 +
 .../analyzer/dsl/spec/parser/YamlParserSpec.java   |   46 +
 .../log/analyzer/dsl/spec/sink/SamplerSpec.java    |    0
 .../oap/log/analyzer/dsl/spec/sink/SinkSpec.java   |    0
 .../dsl/spec/sink/sampler/PossibilitySampler.java  |   54 +
 .../dsl/spec/sink/sampler/RateLimitingSampler.java |  107 ++
 .../analyzer/dsl/spec/sink/sampler/Sampler.java    |   30 +-
 .../oap/log/analyzer/module/LogAnalyzerModule.java |   28 +-
 .../oap/log/analyzer/provider/LALConfig.java       |   18 +-
 .../oap/log/analyzer/provider/LALConfigs.java      |   77 ++
 .../analyzer/provider/LogAnalyzerModuleConfig.java |   74 ++
 .../provider/LogAnalyzerModuleProvider.java        |  102 ++
 .../provider/log/ILogAnalysisListenerManager.java  |   25 +-
 .../analyzer/provider/log/ILogAnalyzerService.java |   23 +-
 .../oap/log/analyzer/provider/log/LogAnalyzer.java |   90 ++
 .../provider/log/LogAnalyzerServiceImpl.java       |   62 ++
 .../provider/log/analyzer/LogAnalyzerFactory.java  |   12 +-
 .../provider/log/listener/LogAnalysisListener.java |   25 +-
 .../log/listener/LogAnalysisListenerFactory.java   |   17 +-
 .../provider/log/listener/LogFilterListener.java   |   96 ++
 .../provider/log/listener/LogSinkListener.java     |   25 +-
 .../log/listener/LogSinkListenerFactory.java       |   14 +-
 .../provider/log/listener/RecordSinkListener.java  |  178 ++++
 .../provider/log/listener/TrafficSinkListener.java |  118 +++
 ...ywalking.oap.server.library.module.ModuleDefine |    5 +-
 ...alking.oap.server.library.module.ModuleProvider |    6 +-
 .../oap/log/analyzer/dsl/DSLSecurityTest.java      |    0
 .../skywalking/oap/log/analyzer/dsl/DSLTest.java   |    0
 .../test/resources/log-mal-rules/placeholder.yaml  |    0
 .../org.mockito.plugins.MockMaker                  |    0
 .../script-compiler}/mal-lal-v1-v2-checker/pom.xml |   19 +-
 .../oap/server/checker/InMemoryCompiler.java       |    0
 .../oap/server/checker/lal/LalComparisonTest.java  |    0
 .../oap/server/checker/mal/MalComparisonTest.java  |    0
 .../checker/mal/MalFilterComparisonTest.java       |    0
 .../script-compiler/mal-v1-with-groovy}/pom.xml    |    8 +-
 .../skywalking/oap/meter/analyzer/Analyzer.java    |    0
 .../oap/meter/analyzer/MetricConvert.java          |  130 +++
 .../oap/meter/analyzer/MetricRuleConfig.java       |   50 +-
 .../skywalking/oap/meter/analyzer/dsl/DSL.java     |    0
 .../oap/meter/analyzer/dsl/DownsamplingType.java   |   10 +-
 .../EndpointEntityDescription.java                 |   44 +
 .../dsl/EntityDescription/EntityDescription.java   |   16 +-
 .../InstanceEntityDescription.java                 |   48 +
 .../ProcessEntityDescription.java                  |   49 +
 .../ProcessRelationEntityDescription.java          |   49 +
 .../ServiceEntityDescription.java                  |   29 +-
 .../ServiceRelationEntityDescription.java          |   53 +
 .../oap/meter/analyzer/dsl/Expression.java         |    0
 .../analyzer/dsl/ExpressionParsingContext.java     |    0
 .../analyzer/dsl/ExpressionParsingException.java   |   12 +-
 .../oap/meter/analyzer/dsl/FilterExpression.java   |    0
 .../oap/meter/analyzer/dsl/MalExpression.java      |    0
 .../oap/meter/analyzer/dsl/MalFilter.java          |    8 +-
 .../oap/meter/analyzer/dsl/NumberClosure.java      |    0
 .../skywalking/oap/meter/analyzer/dsl/Result.java  |   82 ++
 .../skywalking/oap/meter/analyzer/dsl/Sample.java  |   60 ++
 .../oap/meter/analyzer/dsl/SampleFamily.java       |    0
 .../meter/analyzer/dsl/SampleFamilyBuilder.java    |   33 +-
 .../meter/analyzer/dsl/SampleFamilyFunctions.java  |   77 ++
 .../meter/analyzer/dsl/counter/CounterWindow.java  |   88 ++
 .../oap/meter/analyzer/dsl/counter/ID.java         |   22 +-
 .../analyzer/dsl/registry/ProcessRegistry.java     |   86 ++
 .../meter/analyzer/dsl/tagOpt/K8sRetagType.java    |   52 +
 .../oap/meter/analyzer/dsl/tagOpt/Retag.java       |   15 +-
 .../oap/meter/analyzer/k8s/K8sInfoRegistry.java    |  161 +++
 .../prometheus/PrometheusMetricConverter.java      |  152 +++
 .../analyzer/prometheus/rule/MetricsRule.java      |   21 +-
 .../oap/meter/analyzer/prometheus/rule/Rule.java   |   24 +-
 .../oap/meter/analyzer/prometheus/rule/Rules.java  |  120 +++
 .../oap/meter/analyzer/MetricConvertTest.java      |    0
 .../oap/meter/analyzer/dsl/AggregationTest.java    |    0
 .../oap/meter/analyzer/dsl/AnalyzerTest.java       |    0
 .../oap/meter/analyzer/dsl/ArithmeticTest.java     |    0
 .../oap/meter/analyzer/dsl/BasicTest.java          |    0
 .../oap/meter/analyzer/dsl/DecorateTest.java       |    0
 .../meter/analyzer/dsl/ExpressionParsingTest.java  |    0
 .../oap/meter/analyzer/dsl/FilterTest.java         |    0
 .../oap/meter/analyzer/dsl/FunctionTest.java       |    0
 .../oap/meter/analyzer/dsl/IncreaseTest.java       |    0
 .../oap/meter/analyzer/dsl/K8sTagTest.java         |    0
 .../oap/meter/analyzer/dsl/ScopeTest.java          |    0
 .../oap/meter/analyzer/dsl/TagFilterTest.java      |    0
 .../oap/meter/analyzer/dsl/ValueFilterTest.java    |    0
 .../analyzer/dsl/counter/CounterWindowTest.java    |    0
 .../analyzer/dsl/rule/RuleLoaderFailTest.java      |    0
 .../meter/analyzer/dsl/rule/RuleLoaderTest.java    |    0
 .../analyzer/dsl/rule/RuleLoaderYAMLFailTest.java  |    0
 .../org.mockito.plugins.MockMaker                  |    0
 .../resources/otel-rules/illegal-yaml/test.yml     |    0
 .../resources/otel-rules/single-file-case.yaml     |    0
 .../resources/otel-rules/test-folder/case1.yaml    |    0
 .../resources/otel-rules/test-folder/case2.yml     |    0
 .../resources/otel-rules/test-folder/case3.yaml    |    0
 .../test-folder/deeperFolder/caseUnReach.yaml      |    0
 .../resources/otel-rules/test-folder/empty.yaml    |    0
 .../hierarchy-v2 => test/script-compiler}/pom.xml  |   21 +-
 178 files changed, 10545 insertions(+), 5511 deletions(-)

diff --git a/docs/en/academy/groovy-replacement-plan.md b/docs/en/academy/groovy-replacement-plan.md
index 1018615038..f656a7b94d 100644
--- a/docs/en/academy/groovy-replacement-plan.md
+++ b/docs/en/academy/groovy-replacement-plan.md
@@ -606,6 +606,120 @@ v1 (Groovy) and v2 (Java) never coexist in the OAP runtime classpath. The `mal-l
 7. **Remove `LALDelegatingScript.java`**: replaced by `LalExpression` interface
 8. **Verify `server-core` has zero Groovy imports**: `HierarchyDefinitionService` and `HierarchyService` now use `BiFunction` only
 
+### Phase 8: Replace v2 Manifest Loading with Real Compilers (ANTLR4 + Javassist)
+
+Phase 7 completed: v2 modules are standalone (zero Groovy), v1 depends on v2. Currently, v2 loads transpiled classes via manifest files (`META-INF/mal-expressions.txt`, `META-INF/lal-expressions.txt`) that were pre-compiled at build time. This prevents on-demand config changes since MAL/LAL/hierarchy configs are in the final package and users may want to modify them.
+
+The goal is to replace this manifest-based approach with **real compilers** following the OAL pattern: ANTLR4 grammar -> parser -> model -> Javassist class generation -> listener notification. This enables runtime compilation when configs change.
+
+#### Module Renaming: Drop `-v2` Suffix
+
+Since v1 modules move to `test/script-compiler/`, the v2 modules become the primary ones and lose the `-v2` suffix:
+- `meter-analyzer-v2` -> `meter-analyzer` (package stays `o.a.s.oap.meter.analyzer`)
+- `log-analyzer-v2` -> `log-analyzer` (package stays `o.a.s.oap.log.analyzer`)
+- `hierarchy-v2` -> `hierarchy` (no v1 name conflict since v1 moves out)
+- `.v2.` sub-packages (`dsl.v2.DSL`, `dsl.v2.Binding`, etc.) merge back into parent packages (`dsl.DSL`, `dsl.Binding`)
+
+#### Target Module Structure
+
+```
+oap-server/
+  mal-grammar/                   NEW — ANTLR4 grammar for MAL expressions
+  lal-grammar/                   NEW — ANTLR4 grammar for LAL scripts
+  hierarchy-rule-grammar/        NEW — ANTLR4 grammar for hierarchy matching rules
+
+oap-server/analyzer/
+  agent-analyzer/                (stays)
+  event-analyzer/                (stays)
+  meter-analyzer/                (renamed from meter-analyzer-v2, runtime MAL, calls mal-compiler)
+  log-analyzer/                  (renamed from log-analyzer-v2, runtime LAL, calls lal-compiler)
+  hierarchy/                     (renamed from hierarchy-v2, calls hierarchy-rule-compiler)
+  mal-compiler/                  NEW — MAL expression compiler engine
+  lal-compiler/                  NEW — LAL script compiler engine
+  hierarchy-rule-compiler/       NEW — hierarchy rule compiler engine
+
+test/script-compiler/            NEW — aggregator for v1/transpiler/checker (not in dist)
+  mal-groovy/                    <- meter-analyzer v1 (Groovy)
+  lal-groovy/                    <- log-analyzer v1 (Groovy)
+  hierarchy-groovy/              <- hierarchy-v1 (Groovy)
+  mal-transpiler/                <- mal-transpiler
+  lal-transpiler/                <- lal-transpiler
+  mal-lal-v1-v2-checker/         <- mal-lal-v1-v2-checker
+  hierarchy-v1-v2-checker/       <- hierarchy-v1-v2-checker
+```
+
+#### Generated Class Grouping by Config File Name
+
+MAL metrics come from YAML config files (e.g., `oap.yaml`, `spring-micrometer.yaml`). Each file contains multiple `metricsRules`. The compiler groups generated classes by source file name.
+
+- **MAL Compiler API**: `MALCompilerEngine.compile(configFileName, MetricRuleConfig)` -> grouped by file
+- **Generated class naming**: `rt.<configFile>.MalExpr_<metricName>`, e.g., `rt.oap.MalExpr_instance_jvm_cpu`
+- **LAL Compiler API**: `LALCompilerEngine.compile(configFileName, List<LALConfig>)` -> grouped by file
+- **Generated class naming**: `rt.<configFile>.LalExpr_<ruleName>`
+
+#### Eliminate `ExpressionParsingContext` ThreadLocal (MAL only)
+
+The current MAL `run()` method serves dual purposes controlled by a ThreadLocal:
+1. **Parse phase** (startup): `ExpressionParsingContext` ThreadLocal is set, `run()` is called with an empty map to discover which metric names the expression references
+2. **Runtime phase** (every ingestion cycle): ThreadLocal is not set, `run()` computes the actual result
+
+This is eliminated by extracting metadata statically. The `MalExpression` interface gains a `metadata()` method:
+
+```java
+public interface MalExpression {
+    /** Pure computation. No side effects. */
+    SampleFamily run(Map<String, SampleFamily> samples);
+
+    /** Compile-time metadata -- sample names, scope, downsampling, etc. */
+    ExpressionMetadata metadata();
+}
+```
+
+The ANTLR4 compiler extracts all metadata from the parse tree at compile time:
+
+| Metadata | Extracted from |
+|--|--|
+| `sampleNames` | Bare identifiers (metric references) |
+| `scopeType` | Terminal method: `.service()`, `.instance()`, `.endpoint()` |
+| `downsampling` | Aggregation arg: `AVG`, `SUM`, `MAX`, etc. |
+| `percentiles` | `.percentile()` call arguments |
+| `isHistogram` | Presence of `.histogram()` in chain |
+
+Generated class emits metadata as static fields:
+
+```java
+public class MalExpr_instance_jvm_cpu implements MalExpression {
+    private static final ExpressionMetadata METADATA = new ExpressionMetadata(
+        List.of("instance_jvm_cpu"),  // sampleNames
+        ScopeType.SERVICE_INSTANCE,   // from .service()/instance() call
+        DownsamplingType.AVG          // from aggregation
+    );
+
+    @Override
+    public ExpressionMetadata metadata() { return METADATA; }
+
+    @Override
+    public SampleFamily run(Map<String, SampleFamily> samples) {
+        return ((SampleFamily) samples.getOrDefault("instance_jvm_cpu", SampleFamily.EMPTY))
+            .sum(List.of("service", "instance"));
+    }
+}
+```
+
+Result: `run()` is pure computation, `metadata()` is static facts, `ExpressionParsingContext` and its ThreadLocal are deleted. No dry run with empty map at startup.
+
+LAL and hierarchy do **not** have this problem -- LAL passes `Binding` explicitly as a parameter, hierarchy rules are stateless lambdas.
+
+#### Implementation Sub-Phases
+
+- 8.1: Rename modules (drop -v2 suffix, flatten .v2. sub-packages)
+- 8.2: Grammar modules (ANTLR4 .g4 files)
+- 8.3: Compiler model + parser (no code gen)
+- 8.4: Javassist code generation (including static `ExpressionMetadata` on generated MAL classes)
+- 8.5: Engine integration (wire compilers into renamed modules, delete `ExpressionParsingContext`)
+- 8.6: Move v1/transpiler/checker to test/script-compiler/
+- 8.7: Cleanup (remove manifests, verify zero Groovy)
+
 ---
 
 ## 6. What Gets Removed from Runtime
@@ -880,7 +994,6 @@ New MAL/LAL YAML rules added to `server-starter/src/main/resources/` are automat
 | LAL YAML files processed | 8 |
 | LAL rules transpiled | 10 (6 unique after SHA-256 dedup) |
 | Hierarchy rules replaced | 4 |
-| Hierarchy rules replaced | 4 |
 | Total generated Java classes | ~1,289 |
 | Comparison test assertions | 1,300+ (MAL: 1,281, LAL: 19, hierarchy: 4 rules x multiple service pairs) |
 | Lines of transpiler code (MAL) | ~1,230 |
@@ -909,4 +1022,4 @@ New MAL/LAL YAML rules added to `server-starter/src/main/resources/` are automat
 4. **Phase 5**: Hierarchy v1/v2 module split -- refactor `server-core` to remove Groovy, create `hierarchy-v1/` (Groovy, checker-only) and `hierarchy-v2/` (Java lambdas, runtime)
 5. **Phase 6**: Build comparison test suites -- `mal-lal-v1-v2-checker/` AND `hierarchy-v1-v2-checker/`
 6. **Phase 7**: Switch `server-starter` from v1 to v2 for all three subsystems (MAL, LAL, hierarchy), remove Groovy from runtime classpath
-7. **Eventually**: Remove `mal-lal-v1`, `hierarchy-v1`, and all checker modules once community confidence is established
+7. **Phase 8**: Replace v2 manifest-based class loading with real ANTLR4 + Javassist compilers following the OAL pattern, enabling runtime compilation when configs change. Rename modules (drop `-v2` suffix), move v1/transpiler/checker to `test/script-compiler/`.
diff --git a/oap-server/analyzer/hierarchy-v2/src/main/java/org/apache/skywalking/oap/server/core/config/JavaHierarchyRuleProvider.java b/oap-server/analyzer/hierarchy-v2/src/main/java/org/apache/skywalking/oap/server/core/config/JavaHierarchyRuleProvider.java
deleted file mode 100644
index d85bdb99c7..0000000000
--- a/oap-server/analyzer/hierarchy-v2/src/main/java/org/apache/skywalking/oap/server/core/config/JavaHierarchyRuleProvider.java
+++ /dev/null
@@ -1,86 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.skywalking.oap.server.core.config;
-
-import java.util.HashMap;
-import java.util.Map;
-import java.util.Objects;
-import java.util.function.BiFunction;
-import org.apache.skywalking.oap.server.core.query.type.Service;
-
-/**
- * Pure Java hierarchy rule provider. Contains a static registry of all known
- * hierarchy matching rules as Java lambdas. Zero Groovy dependency.
- *
- * <p>Rule names must match those in hierarchy-definition.yml auto-matching-rules section.
- * Unknown rule names fail fast at startup with IllegalArgumentException.
- */
-public final class JavaHierarchyRuleProvider implements HierarchyDefinitionService.HierarchyRuleProvider {
-
-    private static final Map<String, BiFunction<Service, Service, Boolean>> RULE_REGISTRY;
-
-    static {
-        RULE_REGISTRY = new HashMap<>();
-
-        // name: { (u, l) -> u.name == l.name }
-        RULE_REGISTRY.put("name",
-            (u, l) -> Objects.equals(u.getName(), l.getName()));
-
-        // short-name: { (u, l) -> u.shortName == l.shortName }
-        RULE_REGISTRY.put("short-name",
-            (u, l) -> Objects.equals(u.getShortName(), l.getShortName()));
-
-        // lower-short-name-remove-ns:
-        // { (u, l) -> { if(l.shortName.lastIndexOf('.') > 0)
-        //     return u.shortName == l.shortName.substring(0, l.shortName.lastIndexOf('.'));
-        //     return false; } }
-        RULE_REGISTRY.put("lower-short-name-remove-ns", (u, l) -> {
-            final String sn = l.getShortName();
-            final int dot = sn.lastIndexOf('.');
-            return dot > 0 && Objects.equals(u.getShortName(), sn.substring(0, dot));
-        });
-
-        // lower-short-name-with-fqdn:
-        // { (u, l) -> { if(u.shortName.lastIndexOf(':') > 0)
-        //     return u.shortName.substring(0, u.shortName.lastIndexOf(':')) == l.shortName.concat('.svc.cluster.local');
-        //     return false; } }
-        RULE_REGISTRY.put("lower-short-name-with-fqdn", (u, l) -> {
-            final String sn = u.getShortName();
-            final int colon = sn.lastIndexOf(':');
-            return colon > 0 && Objects.equals(
-                sn.substring(0, colon),
-                l.getShortName() + ".svc.cluster.local");
-        });
-    }
-
-    @Override
-    public Map<String, BiFunction<Service, Service, Boolean>> buildRules(
-            final Map<String, String> ruleExpressions) {
-        final Map<String, BiFunction<Service, Service, Boolean>> rules = new HashMap<>();
-        ruleExpressions.forEach((name, expression) -> {
-            final BiFunction<Service, Service, Boolean> fn = RULE_REGISTRY.get(name);
-            if (fn == null) {
-                throw new IllegalArgumentException(
-                    "Unknown hierarchy matching rule: " + name
-                        + ". Known rules: " + RULE_REGISTRY.keySet());
-            }
-            rules.put(name, fn);
-        });
-        return rules;
-    }
-}
diff --git a/oap-server/analyzer/hierarchy-v2/src/test/java/org/apache/skywalking/oap/server/core/config/JavaHierarchyRuleProviderTest.java b/oap-server/analyzer/hierarchy-v2/src/test/java/org/apache/skywalking/oap/server/core/config/JavaHierarchyRuleProviderTest.java
deleted file mode 100644
index 699d3af4aa..0000000000
--- a/oap-server/analyzer/hierarchy-v2/src/test/java/org/apache/skywalking/oap/server/core/config/JavaHierarchyRuleProviderTest.java
+++ /dev/null
@@ -1,155 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.skywalking.oap.server.core.config;
-
-import java.util.Map;
-import java.util.function.BiFunction;
-import org.apache.skywalking.oap.server.core.query.type.Service;
-import org.junit.jupiter.api.BeforeEach;
-import org.junit.jupiter.api.Test;
-
-import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.junit.jupiter.api.Assertions.assertFalse;
-import static org.junit.jupiter.api.Assertions.assertThrows;
-import static org.junit.jupiter.api.Assertions.assertTrue;
-
-class JavaHierarchyRuleProviderTest {
-
-    private JavaHierarchyRuleProvider provider;
-
-    @BeforeEach
-    void setUp() {
-        provider = new JavaHierarchyRuleProvider();
-    }
-
-    private Service svc(final String name, final String shortName) {
-        final Service s = new Service();
-        s.setName(name);
-        s.setShortName(shortName);
-        return s;
-    }
-
-    // ---- name rule ----
-
-    @Test
-    void nameRuleMatches() {
-        final Map<String, BiFunction<Service, Service, Boolean>> rules =
-            provider.buildRules(Map.of("name", "{ (u, l) -> u.name == l.name }"));
-        assertTrue(rules.get("name").apply(svc("svc", "svc"), svc("svc", "svc")));
-    }
-
-    @Test
-    void nameRuleDoesNotMatch() {
-        final Map<String, BiFunction<Service, Service, Boolean>> rules =
-            provider.buildRules(Map.of("name", "ignored"));
-        assertFalse(rules.get("name").apply(svc("svc", "svc"), svc("other", "other")));
-    }
-
-    // ---- short-name rule ----
-
-    @Test
-    void shortNameRuleMatches() {
-        final Map<String, BiFunction<Service, Service, Boolean>> rules =
-            provider.buildRules(Map.of("short-name", "ignored"));
-        assertTrue(rules.get("short-name").apply(svc("a", "svc"), svc("b", "svc")));
-    }
-
-    @Test
-    void shortNameRuleDoesNotMatch() {
-        final Map<String, BiFunction<Service, Service, Boolean>> rules =
-            provider.buildRules(Map.of("short-name", "ignored"));
-        assertFalse(rules.get("short-name").apply(svc("a", "svc1"), svc("b", "svc2")));
-    }
-
-    // ---- lower-short-name-remove-ns rule ----
-
-    @Test
-    void lowerShortNameRemoveNsMatches() {
-        final Map<String, BiFunction<Service, Service, Boolean>> rules =
-            provider.buildRules(Map.of("lower-short-name-remove-ns", "ignored"));
-        // l.shortName = "svc.namespace", u.shortName = "svc"
-        assertTrue(rules.get("lower-short-name-remove-ns")
-            .apply(svc("a", "svc"), svc("b", "svc.namespace")));
-    }
-
-    @Test
-    void lowerShortNameRemoveNsNoDot() {
-        final Map<String, BiFunction<Service, Service, Boolean>> rules =
-            provider.buildRules(Map.of("lower-short-name-remove-ns", "ignored"));
-        assertFalse(rules.get("lower-short-name-remove-ns")
-            .apply(svc("a", "svc"), svc("b", "svc")));
-    }
-
-    @Test
-    void lowerShortNameRemoveNsMismatch() {
-        final Map<String, BiFunction<Service, Service, Boolean>> rules =
-            provider.buildRules(Map.of("lower-short-name-remove-ns", "ignored"));
-        assertFalse(rules.get("lower-short-name-remove-ns")
-            .apply(svc("a", "other"), svc("b", "svc.namespace")));
-    }
-
-    // ---- lower-short-name-with-fqdn rule ----
-
-    @Test
-    void lowerShortNameWithFqdnMatches() {
-        final Map<String, BiFunction<Service, Service, Boolean>> rules =
-            provider.buildRules(Map.of("lower-short-name-with-fqdn", "ignored"));
-        // u.shortName = "db:3306", l.shortName = "db" -> "db" == "db.svc.cluster.local"? no
-        // u.shortName = "db:3306", l.shortName should match: u prefix = "db", l + fqdn = "db.svc.cluster.local"
-        assertTrue(rules.get("lower-short-name-with-fqdn")
-            .apply(svc("a", "db.svc.cluster.local:3306"), svc("b", "db")));
-    }
-
-    @Test
-    void lowerShortNameWithFqdnNoColon() {
-        final Map<String, BiFunction<Service, Service, Boolean>> rules =
-            provider.buildRules(Map.of("lower-short-name-with-fqdn", "ignored"));
-        assertFalse(rules.get("lower-short-name-with-fqdn")
-            .apply(svc("a", "db"), svc("b", "db")));
-    }
-
-    @Test
-    void lowerShortNameWithFqdnWrongSuffix() {
-        final Map<String, BiFunction<Service, Service, Boolean>> rules =
-            provider.buildRules(Map.of("lower-short-name-with-fqdn", "ignored"));
-        assertFalse(rules.get("lower-short-name-with-fqdn")
-            .apply(svc("a", "db:3306"), svc("b", "other")));
-    }
-
-    // ---- unknown rule ----
-
-    @Test
-    void unknownRuleThrows() {
-        assertThrows(IllegalArgumentException.class,
-            () -> provider.buildRules(Map.of("unknown-rule", "ignored")));
-    }
-
-    // ---- builds all 4 rules ----
-
-    @Test
-    void buildsAllFourRules() {
-        final Map<String, BiFunction<Service, Service, Boolean>> rules =
-            provider.buildRules(Map.of(
-                "name", "ignored",
-                "short-name", "ignored",
-                "lower-short-name-remove-ns", "ignored",
-                "lower-short-name-with-fqdn", "ignored"
-            ));
-        assertEquals(4, rules.size());
-    }
-}
diff --git a/oap-server/analyzer/hierarchy/CLAUDE.md b/oap-server/analyzer/hierarchy/CLAUDE.md
new file mode 100644
index 0000000000..f2bf5d1d9a
--- /dev/null
+++ b/oap-server/analyzer/hierarchy/CLAUDE.md
@@ -0,0 +1,113 @@
+# Hierarchy Rule Compiler
+
+Compiles hierarchy matching rule expressions into `BiFunction<Service, Service, Boolean>` implementation classes at runtime using ANTLR4 parsing and Javassist bytecode generation.
+
+## Compilation Workflow
+
+```
+Rule expression string  (e.g., "{ (u, l) -> u.name == l.name }")
+  → HierarchyRuleScriptParser.parse(expression)   [ANTLR4 lexer/parser → visitor]
+  → HierarchyRuleModel (immutable AST)
+  → HierarchyRuleClassGenerator.compile(ruleName, expression)
+      1. classPool.makeClass()         — create class implementing BiFunction
+      2. generateApplyMethod(model)    — emit Java source for apply(Object, Object)
+      3. ctClass.toClass(HierarchyRulePackageHolder.class) — load via package anchor
+  → BiFunction<Service, Service, Boolean> instance
+```
+
+The generated class implements:
+```java
+Object apply(Object arg0, Object arg1)
+  // cast internally to Service and returns Boolean
+```
+
+No separate consumer/closure classes are needed — hierarchy rules are simple enough to compile into a single method body.
+
+## File Structure
+
+```
+oap-server/analyzer/hierarchy/
+  src/main/antlr4/.../HierarchyRuleLexer.g4     — ANTLR4 lexer grammar
+  src/main/antlr4/.../HierarchyRuleParser.g4    — ANTLR4 parser grammar
+
+  src/main/java/.../compiler/
+    HierarchyRuleScriptParser.java              — ANTLR4 facade: expression → AST
+    HierarchyRuleModel.java                     — Immutable AST model classes
+    HierarchyRuleClassGenerator.java            — Javassist code generator
+    rt/
+      HierarchyRulePackageHolder.java           — Class loading anchor (empty marker)
+
+  src/test/java/.../compiler/
+    HierarchyRuleScriptParserTest.java          — 5 parser tests
+    HierarchyRuleClassGeneratorTest.java        — 4 generator tests
+```
+
+## Package & Class Naming
+
+| Component | Package / Name |
+|-----------|---------------|
+| Parser/Model/Generator | `org.apache.skywalking.oap.server.core.config.compiler` |
+| Generated classes | `org.apache.skywalking.oap.server.core.config.compiler.rt.HierarchyRule_<N>` |
+| Package holder | `org.apache.skywalking.oap.server.core.config.compiler.rt.HierarchyRulePackageHolder` |
+| Service type | `org.apache.skywalking.oap.server.core.query.type.Service` (in server-core) |
+
+`<N>` is a global `AtomicInteger` counter.
+
+## Code Generation Details
+
+**Field access mapping**: Property access in expressions maps to getter methods:
+- `u.name` → `u.getName()`
+- `l.shortName` → `l.getShortName()`
+- Generic: `x.foo` → `x.getFoo()`
+
+**Comparison operators**: `==` and `!=` use `java.util.Objects.equals()`. Numeric comparisons (`>`, `<`, `>=`, `<=`) emit direct operators.
+
+**Method chains**: `l.shortName.substring(0, l.shortName.lastIndexOf("."))` generates chained Java method calls directly.
+
+## Example
+
+**Input**: `{ (u, l) -> u.name == l.name }`
+
+**Generated `apply()` method**:
+```java
+public Object apply(Object arg0, Object arg1) {
+  Service u = (Service) arg0;
+  Service l = (Service) arg1;
+  return Boolean.valueOf(java.util.Objects.equals(u.getName(), l.getName()));
+}
+```
+
+**Input with block body**: `{ (u, l) -> { if (l.shortName.lastIndexOf(".") > 0) { return u.name == l.shortName.substring(0, l.shortName.lastIndexOf(".")); } return false; } }`
+
+**Generated `apply()` method**:
+```java
+public Object apply(Object arg0, Object arg1) {
+  Service u = (Service) arg0;
+  Service l = (Service) arg1;
+  if (l.getShortName().lastIndexOf(".") > 0) {
+    return Boolean.valueOf(
+        java.util.Objects.equals(
+            u.getName(),
+            l.getShortName().substring(0, l.getShortName().lastIndexOf("."))));
+  }
+  return Boolean.valueOf(false);
+}
+```
+
+## Rule Patterns
+
+Four rule types are defined in `hierarchy-definition.yml`:
+
+| Rule Name | Expression Pattern |
+|-----------|-------------------|
+| `name` | `{ (u, l) -> u.name == l.name }` |
+| `short-name` | `{ (u, l) -> u.shortName == l.shortName }` |
+| `lower-short-name-remove-namespace` | `{ (u, l) -> { if (l.shortName.lastIndexOf(".") > 0) { return u.name == l.shortName.substring(0, l.shortName.lastIndexOf(".")); } return false; } }` |
+| `lower-short-name-with-fqdn` | `{ (u, l) -> u.shortName == l.shortName.concat("." + u.shortName) }` |
+
+## Dependencies
+
+Grammar, compiler, and runtime are merged into this module:
+- ANTLR4 grammar → generates lexer/parser at build time
+- `server-core` — `Service` type
+- `javassist` — bytecode generation
diff --git a/oap-server/analyzer/hierarchy-v1-v2-checker/pom.xml b/oap-server/analyzer/hierarchy/pom.xml
similarity index 66%
copy from oap-server/analyzer/hierarchy-v1-v2-checker/pom.xml
copy to oap-server/analyzer/hierarchy/pom.xml
index a663a0fd1a..04adce9ed0 100644
--- a/oap-server/analyzer/hierarchy-v1-v2-checker/pom.xml
+++ b/oap-server/analyzer/hierarchy/pom.xml
@@ -25,27 +25,41 @@
     </parent>
     <modelVersion>4.0.0</modelVersion>
 
-    <artifactId>hierarchy-v1-v2-checker</artifactId>
-    <description>Dual-path comparison tests: Groovy hierarchy rules (v1) vs Java hierarchy rules (v2)</description>
+    <artifactId>hierarchy</artifactId>
 
     <dependencies>
         <dependency>
             <groupId>org.apache.skywalking</groupId>
-            <artifactId>hierarchy-v1</artifactId>
+            <artifactId>server-core</artifactId>
             <version>${project.version}</version>
-            <scope>test</scope>
         </dependency>
         <dependency>
-            <groupId>org.apache.skywalking</groupId>
-            <artifactId>hierarchy-v2</artifactId>
-            <version>${project.version}</version>
-            <scope>test</scope>
+            <groupId>org.antlr</groupId>
+            <artifactId>antlr4-runtime</artifactId>
         </dependency>
         <dependency>
-            <groupId>org.apache.skywalking</groupId>
-            <artifactId>server-core</artifactId>
-            <version>${project.version}</version>
-            <scope>test</scope>
+            <groupId>org.javassist</groupId>
+            <artifactId>javassist</artifactId>
         </dependency>
     </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.antlr</groupId>
+                <artifactId>antlr4-maven-plugin</artifactId>
+                <configuration>
+                    <visitor>true</visitor>
+                </configuration>
+                <executions>
+                    <execution>
+                        <id>antlr</id>
+                        <goals>
+                            <goal>antlr4</goal>
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
+        </plugins>
+    </build>
 </project>
diff --git a/oap-server/analyzer/hierarchy/src/main/antlr4/org/apache/skywalking/hierarchy/rt/grammar/HierarchyRuleLexer.g4 b/oap-server/analyzer/hierarchy/src/main/antlr4/org/apache/skywalking/hierarchy/rt/grammar/HierarchyRuleLexer.g4
new file mode 100644
index 0000000000..2cd2989992
--- /dev/null
+++ b/oap-server/analyzer/hierarchy/src/main/antlr4/org/apache/skywalking/hierarchy/rt/grammar/HierarchyRuleLexer.g4
@@ -0,0 +1,105 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+// Hierarchy Rule matching expression lexer
+//
+// Covers expressions like:
+//   { (u, l) -> u.name == l.name }
+//   { (u, l) -> { if(l.shortName.lastIndexOf('.') > 0) return u.shortName == l.shortName.substring(0, l.shortName.lastIndexOf('.')); return false; } }
+lexer grammar HierarchyRuleLexer;
+
+@Header {package org.apache.skywalking.hierarchy.rt.grammar;}
+
+// Keywords
+IF:         'if';
+ELSE:       'else';
+RETURN:     'return';
+TRUE:       'true';
+FALSE:      'false';
+
+// Comparison and logical operators
+DEQ:        '==';
+NEQ:        '!=';
+AND:        '&&';
+OR:         '||';
+NOT:        '!';
+GT:         '>';
+LT:         '<';
+GTE:        '>=';
+LTE:        '<=';
+
+// Delimiters
+DOT:        '.';
+COMMA:      ',';
+SEMI:       ';';
+L_PAREN:    '(';
+R_PAREN:    ')';
+L_BRACE:    '{';
+R_BRACE:    '}';
+ARROW:      '->';
+
+// Arithmetic (for substring index arguments)
+PLUS:       '+';
+MINUS:      '-';
+
+// Literals
+NUMBER
+    : Digit+
+    ;
+
+STRING
+    : '\'' (~['\\\r\n] | EscapeSequence)* '\''
+    | '"' (~["\\\r\n] | EscapeSequence)* '"'
+    ;
+
+// Comments
+LINE_COMMENT
+    : '//' ~[\r\n]* -> channel(HIDDEN)
+    ;
+
+BLOCK_COMMENT
+    : '/*' .*? '*/' -> channel(HIDDEN)
+    ;
+
+// Whitespace
+WS
+    : [ \t\r\n]+ -> channel(HIDDEN)
+    ;
+
+// Identifiers
+IDENTIFIER
+    : Letter LetterOrDigit*
+    ;
+
+// Fragments
+fragment EscapeSequence
+    : '\\' [btnfr"'\\]
+    ;
+
+fragment Digit
+    : [0-9]
+    ;
+
+fragment Letter
+    : [a-zA-Z_]
+    ;
+
+fragment LetterOrDigit
+    : Letter
+    | [0-9]
+    ;
diff --git a/oap-server/analyzer/hierarchy/src/main/antlr4/org/apache/skywalking/hierarchy/rt/grammar/HierarchyRuleParser.g4 b/oap-server/analyzer/hierarchy/src/main/antlr4/org/apache/skywalking/hierarchy/rt/grammar/HierarchyRuleParser.g4
new file mode 100644
index 0000000000..7d30c016c1
--- /dev/null
+++ b/oap-server/analyzer/hierarchy/src/main/antlr4/org/apache/skywalking/hierarchy/rt/grammar/HierarchyRuleParser.g4
@@ -0,0 +1,135 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+// Hierarchy Rule matching expression parser
+//
+// Parses expressions from hierarchy-definition.yml auto-matching-rules:
+//   name:                    "{ (u, l) -> u.name == l.name }"
+//   short-name:              "{ (u, l) -> u.shortName == l.shortName }"
+//   lower-short-name-remove-ns:
+//     "{ (u, l) -> { if(l.shortName.lastIndexOf('.') > 0) return u.shortName == l.shortName.substring(0, l.shortName.lastIndexOf('.')); return false; } }"
+//   lower-short-name-with-fqdn:
+//     "{ (u, l) -> { if(u.shortName.lastIndexOf(':') > 0) return u.shortName.substring(0, u.shortName.lastIndexOf(':')) == l.shortName.concat('.svc.cluster.local'); return false; } }"
+parser grammar HierarchyRuleParser;
+
+@Header {package org.apache.skywalking.hierarchy.rt.grammar;}
+
+options { tokenVocab=HierarchyRuleLexer; }
+
+// ==================== Top-level ====================
+
+// { (u, l) -> body }
+matchingRule
+    : L_BRACE L_PAREN param COMMA param R_PAREN ARROW ruleBody R_BRACE EOF
+    ;
+
+param
+    : IDENTIFIER
+    ;
+
+ruleBody
+    : simpleExpression                      // u.name == l.name
+    | blockBody                             // { if(...) ...; return false; }
+    ;
+
+// ==================== Block body ====================
+
+blockBody
+    : L_BRACE statement+ R_BRACE
+    ;
+
+statement
+    : ifStatement
+    | returnStatement
+    ;
+
+ifStatement
+    : IF L_PAREN condition R_PAREN
+        (returnStatement | blockBody)
+      (ELSE IF L_PAREN condition R_PAREN
+        (returnStatement | blockBody)
+      )*
+      (ELSE
+        (returnStatement | blockBody)
+      )?
+    ;
+
+returnStatement
+    : RETURN returnValue SEMI?
+    ;
+
+returnValue
+    : ruleExpr DEQ ruleExpr                  # returnComparison
+    | ruleExpr NEQ ruleExpr                  # returnNeqComparison
+    | ruleExpr                               # returnExpr
+    ;
+
+// ==================== Conditions ====================
+
+condition
+    : condition AND condition                 # condAnd
+    | condition OR condition                  # condOr
+    | NOT condition                           # condNot
+    | L_PAREN condition R_PAREN              # condParen
+    | ruleExpr DEQ ruleExpr                  # condEq
+    | ruleExpr NEQ ruleExpr                  # condNeq
+    | ruleExpr GT ruleExpr                   # condGt
+    | ruleExpr LT ruleExpr                   # condLt
+    | ruleExpr GTE ruleExpr                  # condGte
+    | ruleExpr LTE ruleExpr                  # condLte
+    | ruleExpr                               # condExpr
+    ;
+
+// ==================== Expressions ====================
+
+simpleExpression
+    : ruleExpr DEQ ruleExpr
+    | ruleExpr NEQ ruleExpr
+    ;
+
+ruleExpr
+    : ruleExpr PLUS ruleExpr                 # exprAdd
+    | ruleExpr MINUS ruleExpr                # exprSub
+    | ruleExprPrimary                        # exprPrimary
+    ;
+
+ruleExprPrimary
+    : methodChain                            # exprMethodChain
+    | STRING                                 # exprString
+    | NUMBER                                 # exprNumber
+    | TRUE                                   # exprTrue
+    | FALSE                                  # exprFalse
+    ;
+
+// ==================== Method chains ====================
+
+// u.name, l.shortName, l.shortName.lastIndexOf('.'),
+// u.shortName.substring(0, l.shortName.lastIndexOf(':'))
+// l.shortName.concat('.svc.cluster.local')
+methodChain
+    : IDENTIFIER (DOT chainSegment)+
+    ;
+
+chainSegment
+    : IDENTIFIER L_PAREN argList? R_PAREN    # chainMethodCall
+    | IDENTIFIER                              # chainFieldAccess
+    ;
+
+argList
+    : ruleExpr (COMMA ruleExpr)*
+    ;
diff --git a/oap-server/analyzer/hierarchy/src/main/java/org/apache/skywalking/oap/server/core/config/compiler/HierarchyRuleClassGenerator.java b/oap-server/analyzer/hierarchy/src/main/java/org/apache/skywalking/oap/server/core/config/compiler/HierarchyRuleClassGenerator.java
new file mode 100644
index 0000000000..754bffa278
--- /dev/null
+++ b/oap-server/analyzer/hierarchy/src/main/java/org/apache/skywalking/oap/server/core/config/compiler/HierarchyRuleClassGenerator.java
@@ -0,0 +1,294 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.skywalking.oap.server.core.config.compiler;
+
+import java.util.List;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.function.BiFunction;
+import javassist.ClassPool;
+import javassist.CtClass;
+import javassist.CtNewConstructor;
+import javassist.CtNewMethod;
+import org.apache.skywalking.oap.server.core.config.compiler.rt.HierarchyRulePackageHolder;
+import org.apache.skywalking.oap.server.core.query.type.Service;
+
+/**
+ * Generates {@link BiFunction BiFunction&lt;Service, Service, Boolean&gt;} implementation classes
+ * from {@link HierarchyRuleModel} AST using Javassist bytecode generation.
+ */
+public final class HierarchyRuleClassGenerator {
+
+    private static final AtomicInteger CLASS_COUNTER = new AtomicInteger(0);
+
+    private static final String PACKAGE_PREFIX =
+        "org.apache.skywalking.oap.server.core.config.compiler.rt.";
+
+    private final ClassPool classPool;
+
+    public HierarchyRuleClassGenerator() {
+        this(ClassPool.getDefault());
+    }
+
+    public HierarchyRuleClassGenerator(final ClassPool classPool) {
+        this.classPool = classPool;
+    }
+
+    /**
+     * Compiles a hierarchy rule expression into a BiFunction class.
+     *
+     * @param ruleName   the rule name (e.g., "name", "short-name")
+     * @param expression the rule expression string
+     * @return a BiFunction that matches two Service objects
+     */
+    @SuppressWarnings("unchecked")
+    public BiFunction<Service, Service, Boolean> compile(
+            final String ruleName, final String expression) throws Exception {
+        final HierarchyRuleModel model = HierarchyRuleScriptParser.parse(expression);
+        final String className = PACKAGE_PREFIX + "HierarchyRule_"
+            + CLASS_COUNTER.getAndIncrement();
+
+        final CtClass ctClass = classPool.makeClass(className);
+        ctClass.addInterface(classPool.get("java.util.function.BiFunction"));
+
+        ctClass.addConstructor(CtNewConstructor.defaultConstructor(ctClass));
+
+        final String applyBody = generateApplyMethod(model);
+        ctClass.addMethod(CtNewMethod.make(applyBody, ctClass));
+
+        final Class<?> clazz = ctClass.toClass(HierarchyRulePackageHolder.class);
+        ctClass.detach();
+        return (BiFunction<Service, Service, Boolean>) clazz.getDeclaredConstructor().newInstance();
+    }
+
+    private String generateApplyMethod(final HierarchyRuleModel model) {
+        final StringBuilder sb = new StringBuilder();
+        sb.append("public Object apply(Object arg0, Object arg1) {\n");
+        sb.append("  org.apache.skywalking.oap.server.core.query.type.Service ");
+        sb.append(model.getUpperParam()).append(" = (org.apache.skywalking.oap.server.core.query.type.Service) arg0;\n");
+        sb.append("  org.apache.skywalking.oap.server.core.query.type.Service ");
+        sb.append(model.getLowerParam()).append(" = (org.apache.skywalking.oap.server.core.query.type.Service) arg1;\n");
+
+        generateRuleBody(sb, model.getBody());
+
+        sb.append("}\n");
+        return sb.toString();
+    }
+
+    private void generateRuleBody(final StringBuilder sb,
+                                  final HierarchyRuleModel.RuleBody body) {
+        if (body instanceof HierarchyRuleModel.SimpleComparison) {
+            final HierarchyRuleModel.SimpleComparison cmp =
+                (HierarchyRuleModel.SimpleComparison) body;
+            sb.append("  return Boolean.valueOf(");
+            generateComparison(sb, cmp.getLeft(), cmp.getOp(), cmp.getRight());
+            sb.append(");\n");
+        } else if (body instanceof HierarchyRuleModel.BlockBody) {
+            final HierarchyRuleModel.BlockBody block = (HierarchyRuleModel.BlockBody) body;
+            for (final HierarchyRuleModel.Statement stmt : block.getStatements()) {
+                generateStatement(sb, stmt);
+            }
+        }
+    }
+
+    private void generateStatement(final StringBuilder sb,
+                                   final HierarchyRuleModel.Statement stmt) {
+        if (stmt instanceof HierarchyRuleModel.IfStatement) {
+            generateIfStatement(sb, (HierarchyRuleModel.IfStatement) stmt);
+        } else if (stmt instanceof HierarchyRuleModel.ReturnStatement) {
+            generateReturnStatement(sb, (HierarchyRuleModel.ReturnStatement) stmt);
+        }
+    }
+
+    private void generateIfStatement(final StringBuilder sb,
+                                     final HierarchyRuleModel.IfStatement ifStmt) {
+        sb.append("  if (");
+        generateCondition(sb, ifStmt.getCondition());
+        sb.append(") {\n");
+        for (final HierarchyRuleModel.Statement s : ifStmt.getThenBranch()) {
+            generateStatement(sb, s);
+        }
+        sb.append("  }\n");
+        if (ifStmt.getElseBranch() != null && !ifStmt.getElseBranch().isEmpty()) {
+            sb.append("  else {\n");
+            for (final HierarchyRuleModel.Statement s : ifStmt.getElseBranch()) {
+                generateStatement(sb, s);
+            }
+            sb.append("  }\n");
+        }
+    }
+
+    private void generateReturnStatement(final StringBuilder sb,
+                                         final HierarchyRuleModel.ReturnStatement retStmt) {
+        final HierarchyRuleModel.Expr expr = retStmt.getValue();
+        if (expr instanceof HierarchyRuleModel.SimpleComparison) {
+            final HierarchyRuleModel.SimpleComparison cmp =
+                (HierarchyRuleModel.SimpleComparison) expr;
+            sb.append("  return Boolean.valueOf(");
+            generateComparison(sb, cmp.getLeft(), cmp.getOp(), cmp.getRight());
+            sb.append(");\n");
+        } else if (expr instanceof HierarchyRuleModel.BoolLiteralExpr) {
+            sb.append("  return Boolean.valueOf(")
+              .append(((HierarchyRuleModel.BoolLiteralExpr) expr).isValue())
+              .append(");\n");
+        } else {
+            sb.append("  return Boolean.valueOf(");
+            generateExpr(sb, expr);
+            sb.append(" != null);\n");
+        }
+    }
+
+    private void generateComparison(final StringBuilder sb,
+                                    final HierarchyRuleModel.Expr left,
+                                    final HierarchyRuleModel.CompareOp op,
+                                    final HierarchyRuleModel.Expr right) {
+        switch (op) {
+            case EQ:
+                sb.append("java.util.Objects.equals(");
+                generateExpr(sb, left);
+                sb.append(", ");
+                generateExpr(sb, right);
+                sb.append(")");
+                break;
+            case NEQ:
+                sb.append("!java.util.Objects.equals(");
+                generateExpr(sb, left);
+                sb.append(", ");
+                generateExpr(sb, right);
+                sb.append(")");
+                break;
+            case GT:
+                generateExpr(sb, left);
+                sb.append(" > ");
+                generateExpr(sb, right);
+                break;
+            case LT:
+                generateExpr(sb, left);
+                sb.append(" < ");
+                generateExpr(sb, right);
+                break;
+            case GTE:
+                generateExpr(sb, left);
+                sb.append(" >= ");
+                generateExpr(sb, right);
+                break;
+            case LTE:
+                generateExpr(sb, left);
+                sb.append(" <= ");
+                generateExpr(sb, right);
+                break;
+            default:
+                throw new IllegalArgumentException("Unsupported comparison op: " + op);
+        }
+    }
+
+    private void generateCondition(final StringBuilder sb,
+                                   final HierarchyRuleModel.Condition cond) {
+        if (cond instanceof HierarchyRuleModel.ComparisonCondition) {
+            final HierarchyRuleModel.ComparisonCondition cc =
+                (HierarchyRuleModel.ComparisonCondition) cond;
+            generateComparison(sb, cc.getLeft(), cc.getOp(), cc.getRight());
+        } else if (cond instanceof HierarchyRuleModel.LogicalCondition) {
+            final HierarchyRuleModel.LogicalCondition lc =
+                (HierarchyRuleModel.LogicalCondition) cond;
+            sb.append("(");
+            generateCondition(sb, lc.getLeft());
+            sb.append(lc.getOp() == HierarchyRuleModel.LogicalOp.AND ? " && " : " || ");
+            generateCondition(sb, lc.getRight());
+            sb.append(")");
+        } else if (cond instanceof HierarchyRuleModel.NotCondition) {
+            sb.append("!(");
+            generateCondition(sb, ((HierarchyRuleModel.NotCondition) cond).getInner());
+            sb.append(")");
+        } else if (cond instanceof HierarchyRuleModel.ExprCondition) {
+            generateExpr(sb, ((HierarchyRuleModel.ExprCondition) cond).getExpr());
+        }
+    }
+
+    private void generateExpr(final StringBuilder sb,
+                              final HierarchyRuleModel.Expr expr) {
+        if (expr instanceof HierarchyRuleModel.MethodChainExpr) {
+            generateMethodChainExpr(sb, (HierarchyRuleModel.MethodChainExpr) expr);
+        } else if (expr instanceof HierarchyRuleModel.StringLiteralExpr) {
+            sb.append('"')
+              .append(escapeJava(((HierarchyRuleModel.StringLiteralExpr) expr).getValue()))
+              .append('"');
+        } else if (expr instanceof HierarchyRuleModel.NumberLiteralExpr) {
+            sb.append(((HierarchyRuleModel.NumberLiteralExpr) expr).getValue());
+        } else if (expr instanceof HierarchyRuleModel.BoolLiteralExpr) {
+            sb.append(((HierarchyRuleModel.BoolLiteralExpr) expr).isValue());
+        } else if (expr instanceof HierarchyRuleModel.BinaryExpr) {
+            final HierarchyRuleModel.BinaryExpr bin = (HierarchyRuleModel.BinaryExpr) expr;
+            generateExpr(sb, bin.getLeft());
+            sb.append(bin.getOp() == HierarchyRuleModel.ArithmeticOp.ADD ? " + " : " - ");
+            generateExpr(sb, bin.getRight());
+        } else if (expr instanceof HierarchyRuleModel.SimpleComparison) {
+            final HierarchyRuleModel.SimpleComparison cmp =
+                (HierarchyRuleModel.SimpleComparison) expr;
+            generateComparison(sb, cmp.getLeft(), cmp.getOp(), cmp.getRight());
+        }
+    }
+
+    private void generateMethodChainExpr(final StringBuilder sb,
+                                         final HierarchyRuleModel.MethodChainExpr expr) {
+        sb.append(expr.getTarget());
+        for (final HierarchyRuleModel.ChainSegment seg : expr.getSegments()) {
+            sb.append('.');
+            if (seg instanceof HierarchyRuleModel.FieldAccess) {
+                final String fieldName = ((HierarchyRuleModel.FieldAccess) seg).getName();
+                sb.append(toGetter(fieldName)).append("()");
+            } else if (seg instanceof HierarchyRuleModel.MethodCallSegment) {
+                final HierarchyRuleModel.MethodCallSegment mc =
+                    (HierarchyRuleModel.MethodCallSegment) seg;
+                sb.append(mc.getName()).append('(');
+                final List<HierarchyRuleModel.Expr> args = mc.getArguments();
+                for (int i = 0; i < args.size(); i++) {
+                    if (i > 0) {
+                        sb.append(", ");
+                    }
+                    generateExpr(sb, args.get(i));
+                }
+                sb.append(')');
+            }
+        }
+    }
+
+    private static String toGetter(final String fieldName) {
+        if ("name".equals(fieldName)) {
+            return "getName";
+        } else if ("shortName".equals(fieldName)) {
+            return "getShortName";
+        }
+        return "get" + Character.toUpperCase(fieldName.charAt(0)) + fieldName.substring(1);
+    }
+
+    private static String escapeJava(final String s) {
+        return s.replace("\\", "\\\\")
+                .replace("\"", "\\\"")
+                .replace("\n", "\\n")
+                .replace("\r", "\\r")
+                .replace("\t", "\\t");
+    }
+
+    /**
+     * Generates the Java source body of the apply method for debugging/testing.
+     */
+    public String generateSource(final String expression) {
+        final HierarchyRuleModel model = HierarchyRuleScriptParser.parse(expression);
+        return generateApplyMethod(model);
+    }
+}
diff --git a/oap-server/analyzer/hierarchy/src/main/java/org/apache/skywalking/oap/server/core/config/compiler/HierarchyRuleModel.java b/oap-server/analyzer/hierarchy/src/main/java/org/apache/skywalking/oap/server/core/config/compiler/HierarchyRuleModel.java
new file mode 100644
index 0000000000..d9ecc4d707
--- /dev/null
+++ b/oap-server/analyzer/hierarchy/src/main/java/org/apache/skywalking/oap/server/core/config/compiler/HierarchyRuleModel.java
@@ -0,0 +1,260 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.skywalking.oap.server.core.config.compiler;
+
+import java.util.Collections;
+import java.util.List;
+import lombok.Getter;
+
+/**
+ * Immutable AST model for hierarchy matching rule expressions.
+ * Represents parsed expressions like:
+ * <pre>
+ *   { (u, l) -> u.name == l.name }
+ *   { (u, l) -> { if(l.shortName.lastIndexOf('.') > 0) return ...; return false; } }
+ * </pre>
+ */
+@Getter
+public final class HierarchyRuleModel {
+    private final String upperParam;
+    private final String lowerParam;
+    private final RuleBody body;
+
+    private HierarchyRuleModel(final String upperParam, final String lowerParam, final RuleBody body) {
+        this.upperParam = upperParam;
+        this.lowerParam = lowerParam;
+        this.body = body;
+    }
+
+    public static HierarchyRuleModel of(final String upperParam, final String lowerParam, final RuleBody body) {
+        return new HierarchyRuleModel(upperParam, lowerParam, body);
+    }
+
+    /**
+     * Rule body — either a simple comparison or a block with if/return statements.
+     */
+    public interface RuleBody {
+    }
+
+    /**
+     * Simple comparison body: {@code u.name == l.name}
+     */
+    @Getter
+    public static final class SimpleComparison implements RuleBody, Expr {
+        private final Expr left;
+        private final CompareOp op;
+        private final Expr right;
+
+        public SimpleComparison(final Expr left, final CompareOp op, final Expr right) {
+            this.left = left;
+            this.op = op;
+            this.right = right;
+        }
+    }
+
+    /**
+     * Block body with multiple statements: {@code { if(...) return ...; return false; }}
+     */
+    @Getter
+    public static final class BlockBody implements RuleBody {
+        private final List<Statement> statements;
+
+        public BlockBody(final List<Statement> statements) {
+            this.statements = Collections.unmodifiableList(statements);
+        }
+    }
+
+    // ==================== Statements ====================
+
+    public interface Statement {
+    }
+
+    @Getter
+    public static final class IfStatement implements Statement {
+        private final Condition condition;
+        private final List<Statement> thenBranch;
+        private final List<Statement> elseBranch;
+
+        public IfStatement(final Condition condition,
+                           final List<Statement> thenBranch,
+                           final List<Statement> elseBranch) {
+            this.condition = condition;
+            this.thenBranch = Collections.unmodifiableList(thenBranch);
+            this.elseBranch = elseBranch != null
+                ? Collections.unmodifiableList(elseBranch) : Collections.emptyList();
+        }
+    }
+
+    @Getter
+    public static final class ReturnStatement implements Statement {
+        private final Expr value;
+
+        public ReturnStatement(final Expr value) {
+            this.value = value;
+        }
+    }
+
+    // ==================== Conditions ====================
+
+    public interface Condition {
+    }
+
+    @Getter
+    public static final class ComparisonCondition implements Condition {
+        private final Expr left;
+        private final CompareOp op;
+        private final Expr right;
+
+        public ComparisonCondition(final Expr left, final CompareOp op, final Expr right) {
+            this.left = left;
+            this.op = op;
+            this.right = right;
+        }
+    }
+
+    @Getter
+    public static final class LogicalCondition implements Condition {
+        private final Condition left;
+        private final LogicalOp op;
+        private final Condition right;
+
+        public LogicalCondition(final Condition left, final LogicalOp op, final Condition right) {
+            this.left = left;
+            this.op = op;
+            this.right = right;
+        }
+    }
+
+    @Getter
+    public static final class NotCondition implements Condition {
+        private final Condition inner;
+
+        public NotCondition(final Condition inner) {
+            this.inner = inner;
+        }
+    }
+
+    @Getter
+    public static final class ExprCondition implements Condition {
+        private final Expr expr;
+
+        public ExprCondition(final Expr expr) {
+            this.expr = expr;
+        }
+    }
+
+    // ==================== Expressions ====================
+
+    public interface Expr {
+    }
+
+    /**
+     * Method chain: {@code u.name}, {@code l.shortName.lastIndexOf('.')},
+     * {@code u.shortName.substring(0, l.shortName.lastIndexOf(':'))}
+     */
+    @Getter
+    public static final class MethodChainExpr implements Expr {
+        private final String target;
+        private final List<ChainSegment> segments;
+
+        public MethodChainExpr(final String target, final List<ChainSegment> segments) {
+            this.target = target;
+            this.segments = Collections.unmodifiableList(segments);
+        }
+    }
+
+    @Getter
+    public static final class StringLiteralExpr implements Expr {
+        private final String value;
+
+        public StringLiteralExpr(final String value) {
+            this.value = value;
+        }
+    }
+
+    @Getter
+    public static final class NumberLiteralExpr implements Expr {
+        private final long value;
+
+        public NumberLiteralExpr(final long value) {
+            this.value = value;
+        }
+    }
+
+    @Getter
+    public static final class BoolLiteralExpr implements Expr {
+        private final boolean value;
+
+        public BoolLiteralExpr(final boolean value) {
+            this.value = value;
+        }
+    }
+
+    @Getter
+    public static final class BinaryExpr implements Expr {
+        private final Expr left;
+        private final ArithmeticOp op;
+        private final Expr right;
+
+        public BinaryExpr(final Expr left, final ArithmeticOp op, final Expr right) {
+            this.left = left;
+            this.op = op;
+            this.right = right;
+        }
+    }
+
+    // ==================== Chain segments ====================
+
+    public interface ChainSegment {
+        String getName();
+    }
+
+    @Getter
+    public static final class FieldAccess implements ChainSegment {
+        private final String name;
+
+        public FieldAccess(final String name) {
+            this.name = name;
+        }
+    }
+
+    @Getter
+    public static final class MethodCallSegment implements ChainSegment {
+        private final String name;
+        private final List<Expr> arguments;
+
+        public MethodCallSegment(final String name, final List<Expr> arguments) {
+            this.name = name;
+            this.arguments = Collections.unmodifiableList(arguments);
+        }
+    }
+
+    // ==================== Enums ====================
+
+    public enum CompareOp {
+        EQ, NEQ, GT, LT, GTE, LTE
+    }
+
+    public enum LogicalOp {
+        AND, OR
+    }
+
+    public enum ArithmeticOp {
+        ADD, SUB
+    }
+}
diff --git a/oap-server/analyzer/hierarchy/src/main/java/org/apache/skywalking/oap/server/core/config/compiler/HierarchyRuleScriptParser.java b/oap-server/analyzer/hierarchy/src/main/java/org/apache/skywalking/oap/server/core/config/compiler/HierarchyRuleScriptParser.java
new file mode 100644
index 0000000000..84dd3db2e2
--- /dev/null
+++ b/oap-server/analyzer/hierarchy/src/main/java/org/apache/skywalking/oap/server/core/config/compiler/HierarchyRuleScriptParser.java
@@ -0,0 +1,375 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.skywalking.oap.server.core.config.compiler;
+
+import java.util.ArrayList;
+import java.util.List;
+import org.antlr.v4.runtime.BaseErrorListener;
+import org.antlr.v4.runtime.CharStreams;
+import org.antlr.v4.runtime.CommonTokenStream;
+import org.antlr.v4.runtime.RecognitionException;
+import org.antlr.v4.runtime.Recognizer;
+import org.apache.skywalking.hierarchy.rt.grammar.HierarchyRuleLexer;
+import org.apache.skywalking.hierarchy.rt.grammar.HierarchyRuleParser;
+import org.apache.skywalking.hierarchy.rt.grammar.HierarchyRuleParserBaseVisitor;
+
+/**
+ * Facade: parses hierarchy rule expression strings into {@link HierarchyRuleModel}.
+ *
+ * <pre>
+ *   HierarchyRuleModel model = HierarchyRuleScriptParser.parse(
+ *       "{ (u, l) -> u.name == l.name }");
+ * </pre>
+ */
+public final class HierarchyRuleScriptParser {
+
+    private HierarchyRuleScriptParser() {
+    }
+
+    public static HierarchyRuleModel parse(final String expression) {
+        final HierarchyRuleLexer lexer = new HierarchyRuleLexer(
+            CharStreams.fromString(expression));
+        final CommonTokenStream tokens = new CommonTokenStream(lexer);
+        final HierarchyRuleParser parser = new HierarchyRuleParser(tokens);
+
+        final List<String> errors = new ArrayList<>();
+        parser.removeErrorListeners();
+        parser.addErrorListener(new BaseErrorListener() {
+            @Override
+            public void syntaxError(final Recognizer<?, ?> recognizer,
+                                    final Object offendingSymbol,
+                                    final int line,
+                                    final int charPositionInLine,
+                                    final String msg,
+                                    final RecognitionException e) {
+                errors.add(line + ":" + charPositionInLine + " " + msg);
+            }
+        });
+
+        final HierarchyRuleParser.MatchingRuleContext tree = parser.matchingRule();
+        if (!errors.isEmpty()) {
+            throw new IllegalArgumentException(
+                "Hierarchy rule parsing failed: " + String.join("; ", errors)
+                    + " in expression: " + expression);
+        }
+
+        return new HierarchyRuleModelVisitor().visit(tree);
+    }
+
+    /**
+     * Visitor that transforms the ANTLR4 parse tree into {@link HierarchyRuleModel}.
+     */
+    private static final class HierarchyRuleModelVisitor
+            extends HierarchyRuleParserBaseVisitor<HierarchyRuleModel> {
+
+        @Override
+        public HierarchyRuleModel visitMatchingRule(final HierarchyRuleParser.MatchingRuleContext ctx) {
+            final String upperParam = ctx.param(0).getText();
+            final String lowerParam = ctx.param(1).getText();
+            final HierarchyRuleModel.RuleBody body = convertRuleBody(ctx.ruleBody());
+            return HierarchyRuleModel.of(upperParam, lowerParam, body);
+        }
+
+        private HierarchyRuleModel.RuleBody convertRuleBody(
+                final HierarchyRuleParser.RuleBodyContext ctx) {
+            if (ctx.simpleExpression() != null) {
+                return convertSimpleExpression(ctx.simpleExpression());
+            }
+            return convertBlockBody(ctx.blockBody());
+        }
+
+        private HierarchyRuleModel.SimpleComparison convertSimpleExpression(
+                final HierarchyRuleParser.SimpleExpressionContext ctx) {
+            final HierarchyRuleModel.Expr left = new ExprVisitor().visitRuleExpr(ctx.ruleExpr(0));
+            final HierarchyRuleModel.Expr right = new ExprVisitor().visitRuleExpr(ctx.ruleExpr(1));
+            final HierarchyRuleModel.CompareOp op = ctx.DEQ() != null
+                ? HierarchyRuleModel.CompareOp.EQ : HierarchyRuleModel.CompareOp.NEQ;
+            return new HierarchyRuleModel.SimpleComparison(left, op, right);
+        }
+
+        private HierarchyRuleModel.BlockBody convertBlockBody(
+                final HierarchyRuleParser.BlockBodyContext ctx) {
+            final List<HierarchyRuleModel.Statement> stmts = new ArrayList<>();
+            for (final HierarchyRuleParser.StatementContext stmtCtx : ctx.statement()) {
+                stmts.add(convertStatement(stmtCtx));
+            }
+            return new HierarchyRuleModel.BlockBody(stmts);
+        }
+
+        private HierarchyRuleModel.Statement convertStatement(
+                final HierarchyRuleParser.StatementContext ctx) {
+            if (ctx.ifStatement() != null) {
+                return convertIfStatement(ctx.ifStatement());
+            }
+            return convertReturnStatement(ctx.returnStatement());
+        }
+
+        private HierarchyRuleModel.IfStatement convertIfStatement(
+                final HierarchyRuleParser.IfStatementContext ctx) {
+            final HierarchyRuleModel.Condition condition =
+                new ConditionVisitor().visit(ctx.condition(0));
+
+            final List<HierarchyRuleModel.Statement> thenBranch = new ArrayList<>();
+            if (ctx.returnStatement(0) != null) {
+                thenBranch.add(convertReturnStatement(ctx.returnStatement(0)));
+            } else if (ctx.blockBody(0) != null) {
+                thenBranch.addAll(convertBlockBody(ctx.blockBody(0)).getStatements());
+            }
+
+            final List<HierarchyRuleModel.Statement> elseBranch = new ArrayList<>();
+            // Handle else-if and else branches
+            final int condCount = ctx.condition().size();
+            final int retCount = ctx.returnStatement().size();
+            final int blockCount = ctx.blockBody().size();
+
+            // If there are more conditions (else if branches)
+            if (condCount > 1 || retCount > 1 || blockCount > 1) {
+                // Simplification: flatten else-if into the else branch
+                // For the current hierarchy rules, we don't have else-if patterns
+                // so this handles the basic else case
+                if (retCount > 1) {
+                    elseBranch.add(convertReturnStatement(ctx.returnStatement(retCount - 1)));
+                } else if (blockCount > 1) {
+                    elseBranch.addAll(
+                        convertBlockBody(ctx.blockBody(blockCount - 1)).getStatements());
+                }
+            }
+
+            return new HierarchyRuleModel.IfStatement(condition, thenBranch, elseBranch);
+        }
+
+        private HierarchyRuleModel.ReturnStatement convertReturnStatement(
+                final HierarchyRuleParser.ReturnStatementContext ctx) {
+            final HierarchyRuleParser.ReturnValueContext rv = ctx.returnValue();
+            if (rv instanceof HierarchyRuleParser.ReturnComparisonContext) {
+                final HierarchyRuleParser.ReturnComparisonContext rc =
+                    (HierarchyRuleParser.ReturnComparisonContext) rv;
+                final ExprVisitor ev = new ExprVisitor();
+                final HierarchyRuleModel.SimpleComparison comp =
+                    new HierarchyRuleModel.SimpleComparison(
+                        ev.visitRuleExpr(rc.ruleExpr(0)),
+                        HierarchyRuleModel.CompareOp.EQ,
+                        ev.visitRuleExpr(rc.ruleExpr(1)));
+                return new HierarchyRuleModel.ReturnStatement(comp);
+            }
+            if (rv instanceof HierarchyRuleParser.ReturnNeqComparisonContext) {
+                final HierarchyRuleParser.ReturnNeqComparisonContext rnc =
+                    (HierarchyRuleParser.ReturnNeqComparisonContext) rv;
+                final ExprVisitor ev = new ExprVisitor();
+                final HierarchyRuleModel.SimpleComparison comp =
+                    new HierarchyRuleModel.SimpleComparison(
+                        ev.visitRuleExpr(rnc.ruleExpr(0)),
+                        HierarchyRuleModel.CompareOp.NEQ,
+                        ev.visitRuleExpr(rnc.ruleExpr(1)));
+                return new HierarchyRuleModel.ReturnStatement(comp);
+            }
+            // returnExpr
+            final HierarchyRuleParser.ReturnExprContext re =
+                (HierarchyRuleParser.ReturnExprContext) rv;
+            final HierarchyRuleModel.Expr value = new ExprVisitor().visitRuleExpr(re.ruleExpr());
+            return new HierarchyRuleModel.ReturnStatement(value);
+        }
+    }
+
+    /**
+     * Visitor for condition nodes.
+     */
+    private static final class ConditionVisitor
+            extends HierarchyRuleParserBaseVisitor<HierarchyRuleModel.Condition> {
+
+        @Override
+        public HierarchyRuleModel.Condition visitCondAnd(
+                final HierarchyRuleParser.CondAndContext ctx) {
+            return new HierarchyRuleModel.LogicalCondition(
+                visit(ctx.condition(0)),
+                HierarchyRuleModel.LogicalOp.AND,
+                visit(ctx.condition(1)));
+        }
+
+        @Override
+        public HierarchyRuleModel.Condition visitCondOr(
+                final HierarchyRuleParser.CondOrContext ctx) {
+            return new HierarchyRuleModel.LogicalCondition(
+                visit(ctx.condition(0)),
+                HierarchyRuleModel.LogicalOp.OR,
+                visit(ctx.condition(1)));
+        }
+
+        @Override
+        public HierarchyRuleModel.Condition visitCondNot(
+                final HierarchyRuleParser.CondNotContext ctx) {
+            return new HierarchyRuleModel.NotCondition(visit(ctx.condition()));
+        }
+
+        @Override
+        public HierarchyRuleModel.Condition visitCondParen(
+                final HierarchyRuleParser.CondParenContext ctx) {
+            return visit(ctx.condition());
+        }
+
+        @Override
+        public HierarchyRuleModel.Condition visitCondEq(
+                final HierarchyRuleParser.CondEqContext ctx) {
+            final ExprVisitor ev = new ExprVisitor();
+            return new HierarchyRuleModel.ComparisonCondition(
+                ev.visitRuleExpr(ctx.ruleExpr(0)),
+                HierarchyRuleModel.CompareOp.EQ,
+                ev.visitRuleExpr(ctx.ruleExpr(1)));
+        }
+
+        @Override
+        public HierarchyRuleModel.Condition visitCondNeq(
+                final HierarchyRuleParser.CondNeqContext ctx) {
+            final ExprVisitor ev = new ExprVisitor();
+            return new HierarchyRuleModel.ComparisonCondition(
+                ev.visitRuleExpr(ctx.ruleExpr(0)),
+                HierarchyRuleModel.CompareOp.NEQ,
+                ev.visitRuleExpr(ctx.ruleExpr(1)));
+        }
+
+        @Override
+        public HierarchyRuleModel.Condition visitCondGt(
+                final HierarchyRuleParser.CondGtContext ctx) {
+            final ExprVisitor ev = new ExprVisitor();
+            return new HierarchyRuleModel.ComparisonCondition(
+                ev.visitRuleExpr(ctx.ruleExpr(0)),
+                HierarchyRuleModel.CompareOp.GT,
+                ev.visitRuleExpr(ctx.ruleExpr(1)));
+        }
+
+        @Override
+        public HierarchyRuleModel.Condition visitCondLt(
+                final HierarchyRuleParser.CondLtContext ctx) {
+            final ExprVisitor ev = new ExprVisitor();
+            return new HierarchyRuleModel.ComparisonCondition(
+                ev.visitRuleExpr(ctx.ruleExpr(0)),
+                HierarchyRuleModel.CompareOp.LT,
+                ev.visitRuleExpr(ctx.ruleExpr(1)));
+        }
+
+        @Override
+        public HierarchyRuleModel.Condition visitCondExpr(
+                final HierarchyRuleParser.CondExprContext ctx) {
+            final ExprVisitor ev = new ExprVisitor();
+            return new HierarchyRuleModel.ExprCondition(ev.visitRuleExpr(ctx.ruleExpr()));
+        }
+    }
+
+    /**
+     * Visitor for expression nodes.
+     */
+    private static final class ExprVisitor
+            extends HierarchyRuleParserBaseVisitor<HierarchyRuleModel.Expr> {
+
+        public HierarchyRuleModel.Expr visitRuleExpr(
+                final HierarchyRuleParser.RuleExprContext ctx) {
+            return visit(ctx);
+        }
+
+        @Override
+        public HierarchyRuleModel.Expr visitExprAdd(
+                final HierarchyRuleParser.ExprAddContext ctx) {
+            return new HierarchyRuleModel.BinaryExpr(
+                visit(ctx.ruleExpr(0)),
+                HierarchyRuleModel.ArithmeticOp.ADD,
+                visit(ctx.ruleExpr(1)));
+        }
+
+        @Override
+        public HierarchyRuleModel.Expr visitExprSub(
+                final HierarchyRuleParser.ExprSubContext ctx) {
+            return new HierarchyRuleModel.BinaryExpr(
+                visit(ctx.ruleExpr(0)),
+                HierarchyRuleModel.ArithmeticOp.SUB,
+                visit(ctx.ruleExpr(1)));
+        }
+
+        @Override
+        public HierarchyRuleModel.Expr visitExprPrimary(
+                final HierarchyRuleParser.ExprPrimaryContext ctx) {
+            return visit(ctx.ruleExprPrimary());
+        }
+
+        @Override
+        public HierarchyRuleModel.Expr visitExprMethodChain(
+                final HierarchyRuleParser.ExprMethodChainContext ctx) {
+            return convertMethodChain(ctx.methodChain());
+        }
+
+        @Override
+        public HierarchyRuleModel.Expr visitExprString(
+                final HierarchyRuleParser.ExprStringContext ctx) {
+            return new HierarchyRuleModel.StringLiteralExpr(stripQuotes(ctx.STRING().getText()));
+        }
+
+        @Override
+        public HierarchyRuleModel.Expr visitExprNumber(
+                final HierarchyRuleParser.ExprNumberContext ctx) {
+            return new HierarchyRuleModel.NumberLiteralExpr(Long.parseLong(ctx.NUMBER().getText()));
+        }
+
+        @Override
+        public HierarchyRuleModel.Expr visitExprTrue(
+                final HierarchyRuleParser.ExprTrueContext ctx) {
+            return new HierarchyRuleModel.BoolLiteralExpr(true);
+        }
+
+        @Override
+        public HierarchyRuleModel.Expr visitExprFalse(
+                final HierarchyRuleParser.ExprFalseContext ctx) {
+            return new HierarchyRuleModel.BoolLiteralExpr(false);
+        }
+
+        private HierarchyRuleModel.MethodChainExpr convertMethodChain(
+                final HierarchyRuleParser.MethodChainContext ctx) {
+            final String target = ctx.IDENTIFIER().getText();
+            final List<HierarchyRuleModel.ChainSegment> segments = new ArrayList<>();
+            for (final HierarchyRuleParser.ChainSegmentContext seg : ctx.chainSegment()) {
+                segments.add(convertChainSegment(seg));
+            }
+            return new HierarchyRuleModel.MethodChainExpr(target, segments);
+        }
+
+        private HierarchyRuleModel.ChainSegment convertChainSegment(
+                final HierarchyRuleParser.ChainSegmentContext ctx) {
+            if (ctx instanceof HierarchyRuleParser.ChainMethodCallContext) {
+                final HierarchyRuleParser.ChainMethodCallContext mc =
+                    (HierarchyRuleParser.ChainMethodCallContext) ctx;
+                final String name = mc.IDENTIFIER().getText();
+                final List<HierarchyRuleModel.Expr> args = new ArrayList<>();
+                if (mc.argList() != null) {
+                    for (final HierarchyRuleParser.RuleExprContext argCtx :
+                            mc.argList().ruleExpr()) {
+                        args.add(visit(argCtx));
+                    }
+                }
+                return new HierarchyRuleModel.MethodCallSegment(name, args);
+            }
+            final HierarchyRuleParser.ChainFieldAccessContext fa =
+                (HierarchyRuleParser.ChainFieldAccessContext) ctx;
+            return new HierarchyRuleModel.FieldAccess(fa.IDENTIFIER().getText());
+        }
+    }
+
+    private static String stripQuotes(final String s) {
+        if (s.length() >= 2 && (s.charAt(0) == '\'' || s.charAt(0) == '"')) {
+            return s.substring(1, s.length() - 1);
+        }
+        return s;
+    }
+}
diff --git a/oap-server/analyzer/meter-analyzer/src/main/java/org/apache/skywalking/oap/meter/analyzer/dsl/MalExpression.java b/oap-server/analyzer/hierarchy/src/main/java/org/apache/skywalking/oap/server/core/config/compiler/rt/HierarchyRulePackageHolder.java
similarity index 72%
copy from oap-server/analyzer/meter-analyzer/src/main/java/org/apache/skywalking/oap/meter/analyzer/dsl/MalExpression.java
copy to oap-server/analyzer/hierarchy/src/main/java/org/apache/skywalking/oap/server/core/config/compiler/rt/HierarchyRulePackageHolder.java
index cfaf1d5bee..31d74d245d 100644
--- a/oap-server/analyzer/meter-analyzer/src/main/java/org/apache/skywalking/oap/meter/analyzer/dsl/MalExpression.java
+++ b/oap-server/analyzer/hierarchy/src/main/java/org/apache/skywalking/oap/server/core/config/compiler/rt/HierarchyRulePackageHolder.java
@@ -13,18 +13,14 @@
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
  * limitations under the License.
- *
  */
 
-package org.apache.skywalking.oap.meter.analyzer.dsl;
-
-import java.util.Map;
+package org.apache.skywalking.oap.server.core.config.compiler.rt;
 
 /**
- * Pure Java replacement for Groovy-based MAL DelegatingScript.
- * Each transpiled MAL expression implements this interface.
+ * Empty marker class used as the class loading anchor for Javassist
+ * {@code CtClass.toClass(Class)} on JDK 16+.
+ * Generated hierarchy rule classes are loaded in this package.
  */
-@FunctionalInterface
-public interface MalExpression {
-    SampleFamily run(Map<String, SampleFamily> samples);
+public class HierarchyRulePackageHolder {
 }
diff --git a/oap-server/analyzer/hierarchy/src/test/java/org/apache/skywalking/oap/server/core/config/compiler/HierarchyRuleClassGeneratorTest.java b/oap-server/analyzer/hierarchy/src/test/java/org/apache/skywalking/oap/server/core/config/compiler/HierarchyRuleClassGeneratorTest.java
new file mode 100644
index 0000000000..1054bcb27b
--- /dev/null
+++ b/oap-server/analyzer/hierarchy/src/test/java/org/apache/skywalking/oap/server/core/config/compiler/HierarchyRuleClassGeneratorTest.java
@@ -0,0 +1,122 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.skywalking.oap.server.core.config.compiler;
+
+import java.util.function.BiFunction;
+import javassist.ClassPool;
+import org.apache.skywalking.oap.server.core.query.type.Service;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+class HierarchyRuleClassGeneratorTest {
+
+    private HierarchyRuleClassGenerator generator;
+
+    @BeforeEach
+    void setUp() {
+        generator = new HierarchyRuleClassGenerator(new ClassPool(true));
+    }
+
+    @Test
+    void compileSimpleNameEquality() throws Exception {
+        final BiFunction<Service, Service, Boolean> fn = generator.compile(
+            "name", "{ (u, l) -> u.name == l.name }");
+
+        assertNotNull(fn);
+
+        final Service upper = new Service();
+        upper.setName("svc-a");
+        final Service lower = new Service();
+        lower.setName("svc-a");
+        assertTrue(fn.apply(upper, lower));
+
+        lower.setName("svc-b");
+        assertFalse(fn.apply(upper, lower));
+    }
+
+    @Test
+    void compileShortNameEquality() throws Exception {
+        final BiFunction<Service, Service, Boolean> fn = generator.compile(
+            "short-name", "{ (u, l) -> u.shortName == l.shortName }");
+
+        assertNotNull(fn);
+
+        final Service upper = new Service();
+        upper.setShortName("svc");
+        final Service lower = new Service();
+        lower.setShortName("svc");
+        assertTrue(fn.apply(upper, lower));
+
+        lower.setShortName("other");
+        assertFalse(fn.apply(upper, lower));
+    }
+
+    @Test
+    void compileLowerShortNameRemoveNs() throws Exception {
+        final String expr = "{ (u, l) -> {"
+            + " if (l.shortName.lastIndexOf('.') > 0) {"
+            + "   return u.shortName == l.shortName.substring(0, l.shortName.lastIndexOf('.'));"
+            + " }"
+            + " return false;"
+            + "} }";
+        final BiFunction<Service, Service, Boolean> fn = generator.compile(
+            "lower-short-name-remove-ns", expr);
+
+        assertNotNull(fn);
+
+        final Service upper = new Service();
+        upper.setShortName("svc-a");
+        final Service lower = new Service();
+        lower.setShortName("svc-a.ns1");
+        assertTrue(fn.apply(upper, lower));
+
+        lower.setShortName("svc-b.ns1");
+        assertFalse(fn.apply(upper, lower));
+
+        lower.setShortName("no-dot");
+        assertFalse(fn.apply(upper, lower));
+    }
+
+    @Test
+    void compileLowerShortNameWithFqdn() throws Exception {
+        final String expr = "{ (u, l) -> {"
+            + " if (u.shortName.lastIndexOf(':') > 0) {"
+            + "   return u.shortName.substring(0, u.shortName.lastIndexOf(':'))"
+            + "     == l.shortName.concat('.svc.cluster.local');"
+            + " }"
+            + " return false;"
+            + "} }";
+        final BiFunction<Service, Service, Boolean> fn = generator.compile(
+            "lower-short-name-with-fqdn", expr);
+
+        assertNotNull(fn);
+
+        final Service upper = new Service();
+        upper.setShortName("svc-a.svc.cluster.local:8080");
+        final Service lower = new Service();
+        lower.setShortName("svc-a");
+        assertTrue(fn.apply(upper, lower));
+
+        upper.setShortName("no-port");
+        assertFalse(fn.apply(upper, lower));
+    }
+}
diff --git a/oap-server/analyzer/hierarchy/src/test/java/org/apache/skywalking/oap/server/core/config/compiler/HierarchyRuleScriptParserTest.java b/oap-server/analyzer/hierarchy/src/test/java/org/apache/skywalking/oap/server/core/config/compiler/HierarchyRuleScriptParserTest.java
new file mode 100644
index 0000000000..1cce3c3271
--- /dev/null
+++ b/oap-server/analyzer/hierarchy/src/test/java/org/apache/skywalking/oap/server/core/config/compiler/HierarchyRuleScriptParserTest.java
@@ -0,0 +1,159 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.skywalking.oap.server.core.config.compiler;
+
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertInstanceOf;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+class HierarchyRuleScriptParserTest {
+
+    @Test
+    void parseSimpleNameEquality() {
+        final HierarchyRuleModel model = HierarchyRuleScriptParser.parse(
+            "{ (u, l) -> u.name == l.name }");
+
+        assertEquals("u", model.getUpperParam());
+        assertEquals("l", model.getLowerParam());
+        assertInstanceOf(HierarchyRuleModel.SimpleComparison.class, model.getBody());
+
+        final HierarchyRuleModel.SimpleComparison cmp =
+            (HierarchyRuleModel.SimpleComparison) model.getBody();
+        assertEquals(HierarchyRuleModel.CompareOp.EQ, cmp.getOp());
+
+        final HierarchyRuleModel.MethodChainExpr left =
+            (HierarchyRuleModel.MethodChainExpr) cmp.getLeft();
+        assertEquals("u", left.getTarget());
+        assertEquals(1, left.getSegments().size());
+        assertEquals("name", left.getSegments().get(0).getName());
+
+        final HierarchyRuleModel.MethodChainExpr right =
+            (HierarchyRuleModel.MethodChainExpr) cmp.getRight();
+        assertEquals("l", right.getTarget());
+        assertEquals("name", right.getSegments().get(0).getName());
+    }
+
+    @Test
+    void parseShortNameEquality() {
+        final HierarchyRuleModel model = HierarchyRuleScriptParser.parse(
+            "{ (u, l) -> u.shortName == l.shortName }");
+
+        final HierarchyRuleModel.SimpleComparison cmp =
+            (HierarchyRuleModel.SimpleComparison) model.getBody();
+        final HierarchyRuleModel.MethodChainExpr left =
+            (HierarchyRuleModel.MethodChainExpr) cmp.getLeft();
+        assertEquals("shortName", left.getSegments().get(0).getName());
+    }
+
+    @Test
+    void parseLowerShortNameRemoveNs() {
+        // lower-short-name-remove-ns rule
+        final String expr =
+            "{ (u, l) -> { if(l.shortName.lastIndexOf('.') > 0) "
+                + "return u.shortName == l.shortName.substring(0, l.shortName.lastIndexOf('.')); "
+                + "return false; } }";
+
+        final HierarchyRuleModel model = HierarchyRuleScriptParser.parse(expr);
+        assertEquals("u", model.getUpperParam());
+        assertEquals("l", model.getLowerParam());
+        assertInstanceOf(HierarchyRuleModel.BlockBody.class, model.getBody());
+
+        final HierarchyRuleModel.BlockBody block =
+            (HierarchyRuleModel.BlockBody) model.getBody();
+        assertEquals(2, block.getStatements().size());
+
+        // First statement: if
+        final HierarchyRuleModel.IfStatement ifStmt =
+            (HierarchyRuleModel.IfStatement) block.getStatements().get(0);
+        assertInstanceOf(
+            HierarchyRuleModel.ComparisonCondition.class, ifStmt.getCondition());
+        final HierarchyRuleModel.ComparisonCondition cond =
+            (HierarchyRuleModel.ComparisonCondition) ifStmt.getCondition();
+        assertEquals(HierarchyRuleModel.CompareOp.GT, cond.getOp());
+
+        // Condition left: l.shortName.lastIndexOf('.')
+        final HierarchyRuleModel.MethodChainExpr condLeft =
+            (HierarchyRuleModel.MethodChainExpr) cond.getLeft();
+        assertEquals("l", condLeft.getTarget());
+        assertEquals(2, condLeft.getSegments().size());
+        assertEquals("shortName", condLeft.getSegments().get(0).getName());
+        assertInstanceOf(
+            HierarchyRuleModel.MethodCallSegment.class, condLeft.getSegments().get(1));
+        final HierarchyRuleModel.MethodCallSegment lastIndexOf =
+            (HierarchyRuleModel.MethodCallSegment) condLeft.getSegments().get(1);
+        assertEquals("lastIndexOf", lastIndexOf.getName());
+        assertEquals(1, lastIndexOf.getArguments().size());
+        assertInstanceOf(
+            HierarchyRuleModel.StringLiteralExpr.class, lastIndexOf.getArguments().get(0));
+        assertEquals(".",
+            ((HierarchyRuleModel.StringLiteralExpr) lastIndexOf.getArguments().get(0)).getValue());
+
+        // Then branch: return u.shortName == l.shortName.substring(0, ...)
+        assertEquals(1, ifStmt.getThenBranch().size());
+        assertInstanceOf(
+            HierarchyRuleModel.ReturnStatement.class, ifStmt.getThenBranch().get(0));
+
+        // Second statement: return false
+        final HierarchyRuleModel.ReturnStatement retFalse =
+            (HierarchyRuleModel.ReturnStatement) block.getStatements().get(1);
+        assertInstanceOf(HierarchyRuleModel.BoolLiteralExpr.class, retFalse.getValue());
+        final HierarchyRuleModel.BoolLiteralExpr falseExpr =
+            (HierarchyRuleModel.BoolLiteralExpr) retFalse.getValue();
+        assertTrue(!falseExpr.isValue());
+    }
+
+    @Test
+    void parseLowerShortNameWithFqdn() {
+        // lower-short-name-with-fqdn rule
+        final String expr =
+            "{ (u, l) -> { if(u.shortName.lastIndexOf(':') > 0) "
+                + "return u.shortName.substring(0, u.shortName.lastIndexOf(':')) "
+                + "== l.shortName.concat('.svc.cluster.local'); "
+                + "return false; } }";
+
+        final HierarchyRuleModel model = HierarchyRuleScriptParser.parse(expr);
+        assertInstanceOf(HierarchyRuleModel.BlockBody.class, model.getBody());
+
+        final HierarchyRuleModel.BlockBody block =
+            (HierarchyRuleModel.BlockBody) model.getBody();
+        assertEquals(2, block.getStatements().size());
+
+        // Verify the if condition checks u.shortName.lastIndexOf(':') > 0
+        final HierarchyRuleModel.IfStatement ifStmt =
+            (HierarchyRuleModel.IfStatement) block.getStatements().get(0);
+        final HierarchyRuleModel.ComparisonCondition cond =
+            (HierarchyRuleModel.ComparisonCondition) ifStmt.getCondition();
+        assertEquals(HierarchyRuleModel.CompareOp.GT, cond.getOp());
+
+        // Then branch has a return statement with == comparison
+        final HierarchyRuleModel.ReturnStatement retStmt =
+            (HierarchyRuleModel.ReturnStatement) ifStmt.getThenBranch().get(0);
+        // The return value should be a comparison (u.shortName.substring(...) == l.shortName.concat(...))
+        // But since our grammar wraps returns as expressions, check the structure
+        assertInstanceOf(HierarchyRuleModel.Expr.class, retStmt.getValue());
+    }
+
+    @Test
+    void parseSyntaxErrorThrows() {
+        assertThrows(IllegalArgumentException.class,
+            () -> HierarchyRuleScriptParser.parse("{ invalid }"));
+    }
+}
diff --git a/oap-server/analyzer/lal-transpiler/src/main/java/org/apache/skywalking/oap/server/transpiler/lal/LalToJavaTranspiler.java b/oap-server/analyzer/lal-transpiler/src/main/java/org/apache/skywalking/oap/server/transpiler/lal/LalToJavaTranspiler.java
deleted file mode 100644
index 5df94e71db..0000000000
--- a/oap-server/analyzer/lal-transpiler/src/main/java/org/apache/skywalking/oap/server/transpiler/lal/LalToJavaTranspiler.java
+++ /dev/null
@@ -1,923 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.skywalking.oap.server.transpiler.lal;
-
-import java.io.File;
-import java.io.IOException;
-import java.io.StringWriter;
-import java.nio.file.Files;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.LinkedHashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.stream.Collectors;
-import javax.tools.JavaCompiler;
-import javax.tools.JavaFileObject;
-import javax.tools.StandardJavaFileManager;
-import javax.tools.ToolProvider;
-import lombok.extern.slf4j.Slf4j;
-import org.apache.skywalking.oap.server.transpiler.mal.MalToJavaTranspiler;
-import org.codehaus.groovy.ast.ModuleNode;
-import org.codehaus.groovy.ast.expr.ArgumentListExpression;
-import org.codehaus.groovy.ast.expr.BinaryExpression;
-import org.codehaus.groovy.ast.expr.BooleanExpression;
-import org.codehaus.groovy.ast.expr.CastExpression;
-import org.codehaus.groovy.ast.expr.ClosureExpression;
-import org.codehaus.groovy.ast.expr.ConstantExpression;
-import org.codehaus.groovy.ast.expr.Expression;
-import org.codehaus.groovy.ast.expr.GStringExpression;
-import org.codehaus.groovy.ast.expr.MapEntryExpression;
-import org.codehaus.groovy.ast.expr.MapExpression;
-import org.codehaus.groovy.ast.expr.MethodCallExpression;
-import org.codehaus.groovy.ast.expr.NotExpression;
-import org.codehaus.groovy.ast.expr.PropertyExpression;
-import org.codehaus.groovy.ast.expr.TupleExpression;
-import org.codehaus.groovy.ast.expr.VariableExpression;
-import org.codehaus.groovy.ast.stmt.BlockStatement;
-import org.codehaus.groovy.ast.stmt.EmptyStatement;
-import org.codehaus.groovy.ast.stmt.ExpressionStatement;
-import org.codehaus.groovy.ast.stmt.IfStatement;
-import org.codehaus.groovy.ast.stmt.Statement;
-import org.codehaus.groovy.control.CompilationUnit;
-import org.codehaus.groovy.control.CompilerConfiguration;
-import org.codehaus.groovy.control.Phases;
-import org.codehaus.groovy.syntax.Types;
-
-/**
- * Transpiles LAL (Log Analysis Language) Groovy scripts to Java source code at build time.
- * Parses DSL strings into Groovy AST (via CompilationUnit at CONVERSION phase),
- * walks AST nodes, and produces Java classes implementing LalExpression.
- */
-@Slf4j
-public class LalToJavaTranspiler {
-
-    public static final String GENERATED_PACKAGE =
-        "org.apache.skywalking.oap.server.core.source.oal.rt.lal";
-
-    private static final Set<String> CONSUMER_METHODS = Set.of(
-        "text", "extractor", "sink", "slowSql", "sampledTrace", "metrics", "sampler"
-    );
-
-    private static final Set<String> STATIC_TYPES = Set.of("ProcessRegistry");
-
-    // ---- Batch state ----
-
-    private final Map<String, String> lalSources = new LinkedHashMap<>();
-    private final Map<String, String> hashToClass = new LinkedHashMap<>();
-
-    private int tempVarCounter;
-
-    /**
-     * Transpile a LAL DSL script to a Java class implementing LalExpression.
-     *
-     * @param className simple class name (e.g. "LalExpr_3")
-     * @param dslText   the Groovy DSL string (filter { ... })
-     * @return Java source code
-     */
-    public String transpile(final String className, final String dslText) {
-        tempVarCounter = 0;
-        final ModuleNode module = parseToAST(dslText);
-        final Statement body = extractBody(module);
-
-        final StringBuilder sb = new StringBuilder();
-        emitHeader(sb, className);
-
-        final List<Statement> stmts = flattenStatements(body);
-        for (Statement stmt : stmts) {
-            emitStatement(sb, stmt, "filterSpec", "binding", 2);
-        }
-
-        emitFooter(sb);
-        return sb.toString();
-    }
-
-    public void register(final String className, final String hash, final String source) {
-        lalSources.put(className, source);
-        hashToClass.put(hash, GENERATED_PACKAGE + "." + className);
-    }
-
-    // ---- AST Parsing ----
-
-    ModuleNode parseToAST(final String expression) {
-        final CompilerConfiguration cc = new CompilerConfiguration();
-        final CompilationUnit cu = new CompilationUnit(cc);
-        cu.addSource("Script", expression);
-        cu.compile(Phases.CONVERSION);
-        final List<ModuleNode> modules = cu.getAST().getModules();
-        if (modules.isEmpty()) {
-            throw new IllegalStateException("No AST modules produced");
-        }
-        return modules.get(0);
-    }
-
-    Statement extractBody(final ModuleNode module) {
-        final BlockStatement block = module.getStatementBlock();
-        if (block != null && !block.getStatements().isEmpty()) {
-            return block;
-        }
-        throw new IllegalStateException("Empty AST body");
-    }
-
-    // ---- Code Generation ----
-
-    private void emitHeader(final StringBuilder sb, final String className) {
-        sb.append("package ").append(GENERATED_PACKAGE).append(";\n\n");
-        sb.append("import java.util.Map;\n");
-        sb.append("import org.apache.skywalking.oap.log.analyzer.dsl.Binding;\n");
-        sb.append("import org.apache.skywalking.oap.log.analyzer.dsl.LalExpression;\n");
-        sb.append("import org.apache.skywalking.oap.log.analyzer.dsl.spec.filter.FilterSpec;\n\n");
-        sb.append("@SuppressWarnings(\"unchecked\")\n");
-        sb.append("public class ").append(className).append(" implements LalExpression {\n\n");
-        emitHelpers(sb);
-        sb.append("    @Override\n");
-        sb.append("    public void execute(FilterSpec filterSpec, Binding binding) {\n");
-    }
-
-    private void emitHelpers(final StringBuilder sb) {
-        sb.append("    private static Object getAt(Object obj, String key) {\n");
-        sb.append("        if (obj == null) return null;\n");
-        sb.append("        if (obj instanceof Binding.Parsed) return ((Binding.Parsed) obj).getAt(key);\n");
-        sb.append("        if (obj instanceof Map) return ((Map<String, Object>) obj).get(key);\n");
-        sb.append("        return null;\n");
-        sb.append("    }\n\n");
-
-        sb.append("    private static long toLong(Object obj) {\n");
-        sb.append("        if (obj instanceof Number) return ((Number) obj).longValue();\n");
-        sb.append("        if (obj instanceof String) return Long.parseLong((String) obj);\n");
-        sb.append("        return 0L;\n");
-        sb.append("    }\n\n");
-
-        sb.append("    private static int toInt(Object obj) {\n");
-        sb.append("        if (obj instanceof Number) return ((Number) obj).intValue();\n");
-        sb.append("        if (obj instanceof String) return Integer.parseInt((String) obj);\n");
-        sb.append("        return 0;\n");
-        sb.append("    }\n\n");
-
-        sb.append("    private static boolean toBoolean(Object obj) {\n");
-        sb.append("        if (obj instanceof Boolean) return (Boolean) obj;\n");
-        sb.append("        if (obj instanceof String) return Boolean.parseBoolean((String) obj);\n");
-        sb.append("        return obj != null;\n");
-        sb.append("    }\n\n");
-
-        sb.append("    private static boolean isTruthy(Object obj) {\n");
-        sb.append("        if (obj == null) return false;\n");
-        sb.append("        if (obj instanceof Boolean) return (Boolean) obj;\n");
-        sb.append("        if (obj instanceof String) return !((String) obj).isEmpty();\n");
-        sb.append("        if (obj instanceof Number) return ((Number) obj).doubleValue() != 0;\n");
-        sb.append("        return true;\n");
-        sb.append("    }\n\n");
-
-        sb.append("    private static boolean isNonEmptyString(Object obj) {\n");
-        sb.append("        if (obj == null) return false;\n");
-        sb.append("        String s = obj.toString();\n");
-        sb.append("        return s != null && !s.trim().isEmpty();\n");
-        sb.append("    }\n\n");
-    }
-
-    private void emitFooter(final StringBuilder sb) {
-        sb.append("    }\n");
-        sb.append("}\n");
-    }
-
-    private void emitStatement(final StringBuilder sb, final Statement stmt,
-                               final String receiver, final String bindingVar, final int indent) {
-        if (stmt instanceof BlockStatement) {
-            for (Statement s : ((BlockStatement) stmt).getStatements()) {
-                emitStatement(sb, s, receiver, bindingVar, indent);
-            }
-        } else if (stmt instanceof ExpressionStatement) {
-            emitExpressionStatement(sb, ((ExpressionStatement) stmt).getExpression(),
-                receiver, bindingVar, indent);
-        } else if (stmt instanceof IfStatement) {
-            emitIfStatement(sb, (IfStatement) stmt, receiver, bindingVar, indent);
-        } else if (stmt instanceof EmptyStatement) {
-            // skip
-        } else {
-            throw new UnsupportedOperationException(
-                "Unsupported statement: " + stmt.getClass().getSimpleName());
-        }
-    }
-
-    private void emitExpressionStatement(final StringBuilder sb, final Expression expr,
-                                         final String receiver, final String bindingVar,
-                                         final int indent) {
-        if (expr instanceof MethodCallExpression) {
-            emitMethodCall(sb, (MethodCallExpression) expr, receiver, bindingVar, indent);
-        } else if (expr instanceof BinaryExpression) {
-            emitBinaryAsNamedArg(sb, (BinaryExpression) expr, receiver, bindingVar, indent);
-        } else {
-            throw new UnsupportedOperationException(
-                "Unsupported expression statement: " + expr.getClass().getSimpleName()
-                    + " (" + expr.getText() + ")");
-        }
-    }
-
-    private void emitMethodCall(final StringBuilder sb, final MethodCallExpression mce,
-                                final String receiver, final String bindingVar, final int indent) {
-        final String methodName = mce.getMethodAsString();
-        final Expression objExpr = mce.getObjectExpression();
-        final ArgumentListExpression args = toArgList(mce.getArguments());
-        final List<Expression> argExprs = args.getExpressions();
-
-        // Top-level filter { ... } -> unwrap and emit body
-        if ("filter".equals(methodName) && isThisOrImplicit(objExpr)) {
-            if (!argExprs.isEmpty() && argExprs.get(0) instanceof ClosureExpression) {
-                final ClosureExpression closure = (ClosureExpression) argExprs.get(0);
-                emitStatement(sb, closure.getCode(), receiver, bindingVar, indent);
-                return;
-            }
-        }
-
-        // json {} -> filterSpec.json()
-        if ("json".equals(methodName) && isThisOrImplicit(objExpr)) {
-            indent(sb, indent);
-            sb.append(receiver).append(".json();\n");
-            return;
-        }
-
-        // text { regexp /pattern/ } -> filterSpec.text(tp -> tp.regexp("pattern"))
-        if ("text".equals(methodName) && isThisOrImplicit(objExpr)
-                && !argExprs.isEmpty() && argExprs.get(0) instanceof ClosureExpression) {
-            final ClosureExpression closure = (ClosureExpression) argExprs.get(0);
-            final String tpVar = "tp";
-            indent(sb, indent);
-            sb.append(receiver).append(".text(").append(tpVar).append(" -> {\n");
-            emitStatement(sb, closure.getCode(), tpVar, bindingVar, indent + 1);
-            indent(sb, indent);
-            sb.append("});\n");
-            return;
-        }
-
-        // regexp /pattern/ -> tp.regexp("pattern")
-        if ("regexp".equals(methodName) && isThisOrImplicit(objExpr)) {
-            indent(sb, indent);
-            sb.append(receiver).append(".regexp(");
-            if (!argExprs.isEmpty()) {
-                sb.append(visitValueExpression(argExprs.get(0), bindingVar));
-            }
-            sb.append(");\n");
-            return;
-        }
-
-        // Consumer overload methods: extractor, sink, slowSql, sampledTrace, metrics, sampler
-        if (CONSUMER_METHODS.contains(methodName) && isThisOrImplicit(objExpr)
-                && !argExprs.isEmpty() && argExprs.get(0) instanceof ClosureExpression) {
-            final ClosureExpression closure = (ClosureExpression) argExprs.get(0);
-            final String lambdaVar = lambdaVarFor(methodName);
-            final String childReceiver = childReceiverFor(lambdaVar);
-            indent(sb, indent);
-            sb.append(receiver).append(".").append(methodName).append("(")
-              .append(lambdaVar).append(" -> {\n");
-            emitStatement(sb, closure.getCode(), childReceiver, bindingVar, indent + 1);
-            indent(sb, indent);
-            sb.append("});\n");
-            return;
-        }
-
-        // rateLimit("${expr}") { rpm N } -> sp.rateLimit(idExpr, rls -> rls.rpm(N))
-        if ("rateLimit".equals(methodName) && isThisOrImplicit(objExpr)) {
-            final Expression idArg = argExprs.get(0);
-            final ClosureExpression closure = argExprs.size() > 1
-                && argExprs.get(1) instanceof ClosureExpression
-                ? (ClosureExpression) argExprs.get(1) : null;
-
-            indent(sb, indent);
-            sb.append(receiver).append(".rateLimit(")
-              .append(visitValueExpression(idArg, bindingVar));
-            if (closure != null) {
-                final String rlsVar = "rls";
-                sb.append(", ").append(rlsVar).append(" -> {\n");
-                emitStatement(sb, closure.getCode(), rlsVar, bindingVar, indent + 1);
-                indent(sb, indent);
-                sb.append("}");
-            }
-            sb.append(");\n");
-            return;
-        }
-
-        // abort {} -> filterSpec.abort()
-        if ("abort".equals(methodName) && isThisOrImplicit(objExpr)) {
-            indent(sb, indent);
-            sb.append(receiver).append(".abort();\n");
-            return;
-        }
-
-        // enforcer {} or dropper {} -> sink.enforcer() / sink.dropper()
-        if (("enforcer".equals(methodName) || "dropper".equals(methodName))
-                && isThisOrImplicit(objExpr)) {
-            indent(sb, indent);
-            sb.append(receiver).append(".").append(methodName).append("();\n");
-            return;
-        }
-
-        // tag("KEY") as standalone statement
-        if ("tag".equals(methodName) && isThisOrImplicit(objExpr)
-                && !argExprs.isEmpty() && argExprs.get(0) instanceof ConstantExpression) {
-            indent(sb, indent);
-            sb.append(receiver).append(".tag(\"")
-              .append(MalToJavaTranspiler.escapeJava(argExprs.get(0).getText()))
-              .append("\");\n");
-            return;
-        }
-
-        // Simple value-setting methods: service(val), layer(val), timestamp(val), etc.
-        if (isThisOrImplicit(objExpr)) {
-            indent(sb, indent);
-            sb.append(receiver).append(".").append(methodName).append("(");
-            for (int i = 0; i < argExprs.size(); i++) {
-                if (i > 0) {
-                    sb.append(", ");
-                }
-                sb.append(visitValueExpression(argExprs.get(i), bindingVar));
-            }
-            sb.append(");\n");
-            return;
-        }
-
-        // Static method: ProcessRegistry.generateVirtualLocalProcess(...)
-        if (objExpr instanceof VariableExpression
-                && STATIC_TYPES.contains(((VariableExpression) objExpr).getName())) {
-            indent(sb, indent);
-            sb.append("org.apache.skywalking.oap.meter.analyzer.dsl.registry.ProcessRegistry.")
-              .append(methodName).append("(");
-            for (int i = 0; i < argExprs.size(); i++) {
-                if (i > 0) {
-                    sb.append(", ");
-                }
-                sb.append(visitValueExpression(argExprs.get(i), bindingVar));
-            }
-            sb.append(");\n");
-            return;
-        }
-
-        throw new UnsupportedOperationException(
-            "Unsupported method call: " + methodName + " on " + objExpr.getClass().getSimpleName()
-                + " (" + mce.getText() + ")");
-    }
-
-    private void emitBinaryAsNamedArg(final StringBuilder sb, final BinaryExpression expr,
-                                      final String receiver, final String bindingVar,
-                                      final int indent) {
-        throw new UnsupportedOperationException(
-            "Unsupported binary expression as statement: " + expr.getText());
-    }
-
-    // ---- If/Else ----
-
-    private void emitIfStatement(final StringBuilder sb, final IfStatement ifStmt,
-                                 final String receiver, final String bindingVar, final int indent) {
-        indent(sb, indent);
-        sb.append("if (");
-        sb.append(visitCondition(ifStmt.getBooleanExpression(), bindingVar));
-        sb.append(") {\n");
-
-        emitStatement(sb, ifStmt.getIfBlock(), receiver, bindingVar, indent + 1);
-
-        final Statement elseBlock = ifStmt.getElseBlock();
-        if (elseBlock != null && !(elseBlock instanceof EmptyStatement)) {
-            indent(sb, indent);
-            if (elseBlock instanceof IfStatement) {
-                sb.append("} else ");
-                emitIfStatementInline(sb, (IfStatement) elseBlock, receiver, bindingVar, indent);
-                return;
-            } else {
-                sb.append("} else {\n");
-                emitStatement(sb, elseBlock, receiver, bindingVar, indent + 1);
-                indent(sb, indent);
-                sb.append("}\n");
-            }
-        } else {
-            indent(sb, indent);
-            sb.append("}\n");
-        }
-    }
-
-    private void emitIfStatementInline(final StringBuilder sb, final IfStatement ifStmt,
-                                       final String receiver, final String bindingVar,
-                                       final int indent) {
-        sb.append("if (");
-        sb.append(visitCondition(ifStmt.getBooleanExpression(), bindingVar));
-        sb.append(") {\n");
-
-        emitStatement(sb, ifStmt.getIfBlock(), receiver, bindingVar, indent + 1);
-
-        final Statement elseBlock = ifStmt.getElseBlock();
-        if (elseBlock != null && !(elseBlock instanceof EmptyStatement)) {
-            indent(sb, indent);
-            if (elseBlock instanceof IfStatement) {
-                sb.append("} else ");
-                emitIfStatementInline(sb, (IfStatement) elseBlock, receiver, bindingVar, indent);
-            } else {
-                sb.append("} else {\n");
-                emitStatement(sb, elseBlock, receiver, bindingVar, indent + 1);
-                indent(sb, indent);
-                sb.append("}\n");
-            }
-        } else {
-            indent(sb, indent);
-            sb.append("}\n");
-        }
-    }
-
-    // ---- Condition Visiting ----
-
-    private String visitCondition(final BooleanExpression boolExpr, final String bindingVar) {
-        return visitConditionExpr(boolExpr.getExpression(), bindingVar);
-    }
-
-    String visitConditionExpr(final Expression expr, final String bindingVar) {
-        if (expr instanceof BinaryExpression) {
-            final BinaryExpression bin = (BinaryExpression) expr;
-            final int op = bin.getOperation().getType();
-
-            if (op == Types.COMPARE_EQUAL) {
-                final String left = visitValueExpression(bin.getLeftExpression(), bindingVar);
-                final String right = visitValueExpression(bin.getRightExpression(), bindingVar);
-                if (bin.getRightExpression() instanceof ConstantExpression
-                        && ((ConstantExpression) bin.getRightExpression()).getValue() instanceof String) {
-                    return "\"" + MalToJavaTranspiler.escapeJava(
-                        (String) ((ConstantExpression) bin.getRightExpression()).getValue())
-                        + "\".equals(" + left + ")";
-                }
-                return "java.util.Objects.equals(" + left + ", " + right + ")";
-            }
-
-            if (op == Types.COMPARE_NOT_EQUAL) {
-                final String left = visitValueExpression(bin.getLeftExpression(), bindingVar);
-                final String right = visitValueExpression(bin.getRightExpression(), bindingVar);
-                if (bin.getRightExpression() instanceof ConstantExpression
-                        && ((ConstantExpression) bin.getRightExpression()).getValue() instanceof String) {
-                    return "!\"" + MalToJavaTranspiler.escapeJava(
-                        (String) ((ConstantExpression) bin.getRightExpression()).getValue())
-                        + "\".equals(" + left + ")";
-                }
-                return "!java.util.Objects.equals(" + left + ", " + right + ")";
-            }
-
-            if (op == Types.COMPARE_LESS_THAN) {
-                final String left = visitValueExpression(bin.getLeftExpression(), bindingVar);
-                final String right = visitValueExpression(bin.getRightExpression(), bindingVar);
-                return "toInt(" + left + ") < " + right;
-            }
-
-            if (op == Types.COMPARE_GREATER_THAN_EQUAL) {
-                final String left = visitValueExpression(bin.getLeftExpression(), bindingVar);
-                final String right = visitValueExpression(bin.getRightExpression(), bindingVar);
-                return "toInt(" + left + ") >= " + right;
-            }
-
-            if (op == Types.LOGICAL_AND) {
-                return visitConditionExpr(bin.getLeftExpression(), bindingVar)
-                    + " && " + visitConditionExpr(bin.getRightExpression(), bindingVar);
-            }
-
-            if (op == Types.LOGICAL_OR) {
-                return visitConditionExpr(bin.getLeftExpression(), bindingVar)
-                    + " || " + visitConditionExpr(bin.getRightExpression(), bindingVar);
-            }
-
-            throw new UnsupportedOperationException(
-                "Unsupported condition operator: " + bin.getOperation().getText());
-        }
-
-        if (expr instanceof NotExpression) {
-            final String inner = visitConditionExpr(((NotExpression) expr).getExpression(), bindingVar);
-            return "!" + inner;
-        }
-
-        if (expr instanceof BooleanExpression) {
-            return visitConditionExpr(((BooleanExpression) expr).getExpression(), bindingVar);
-        }
-
-        if (expr instanceof MethodCallExpression) {
-            final MethodCallExpression mce = (MethodCallExpression) expr;
-            final String methodName = mce.getMethodAsString();
-            if ("toString".equals(methodName) || "trim".equals(methodName)) {
-                final String obj = visitValueExpression(mce.getObjectExpression(), bindingVar);
-                return "isNonEmptyString(" + obj + ")";
-            }
-            return visitValueExpression(expr, bindingVar);
-        }
-
-        return "isTruthy(" + visitValueExpression(expr, bindingVar) + ")";
-    }
-
-    // ---- Value Expression Visiting ----
-
-    String visitValueExpression(final Expression expr, final String bindingVar) {
-        if (expr instanceof ConstantExpression) {
-            return visitConstant((ConstantExpression) expr);
-        }
-
-        if (expr instanceof VariableExpression) {
-            return visitVariable((VariableExpression) expr, bindingVar);
-        }
-
-        if (expr instanceof PropertyExpression) {
-            return visitProperty((PropertyExpression) expr, bindingVar);
-        }
-
-        if (expr instanceof CastExpression) {
-            return visitCast((CastExpression) expr, bindingVar);
-        }
-
-        if (expr instanceof MethodCallExpression) {
-            return visitMethodCallValue((MethodCallExpression) expr, bindingVar);
-        }
-
-        if (expr instanceof GStringExpression) {
-            return visitGString((GStringExpression) expr, bindingVar);
-        }
-
-        if (expr instanceof MapExpression) {
-            return visitMapExpression((MapExpression) expr, bindingVar);
-        }
-
-        if (expr instanceof BinaryExpression) {
-            final BinaryExpression bin = (BinaryExpression) expr;
-            final int op = bin.getOperation().getType();
-            if (op == Types.COMPARE_EQUAL || op == Types.COMPARE_NOT_EQUAL
-                    || op == Types.LOGICAL_AND || op == Types.LOGICAL_OR
-                    || op == Types.COMPARE_LESS_THAN || op == Types.COMPARE_GREATER_THAN_EQUAL) {
-                return visitConditionExpr(expr, bindingVar);
-            }
-        }
-
-        if (expr instanceof NotExpression) {
-            return "!" + visitValueExpression(((NotExpression) expr).getExpression(), bindingVar);
-        }
-
-        throw new UnsupportedOperationException(
-            "Unsupported value expression: " + expr.getClass().getSimpleName()
-                + " (" + expr.getText() + ")");
-    }
-
-    private String visitConstant(final ConstantExpression expr) {
-        final Object value = expr.getValue();
-        if (value instanceof String) {
-            return "\"" + MalToJavaTranspiler.escapeJava((String) value) + "\"";
-        }
-        if (value instanceof Integer) {
-            return value.toString();
-        }
-        if (value instanceof Long) {
-            return value + "L";
-        }
-        if (value instanceof Boolean) {
-            return value.toString();
-        }
-        if (value instanceof Double) {
-            return value + "d";
-        }
-        if (value == null) {
-            return "null";
-        }
-        return value.toString();
-    }
-
-    private String visitVariable(final VariableExpression expr, final String bindingVar) {
-        final String name = expr.getName();
-        if ("parsed".equals(name)) {
-            return bindingVar + ".parsed()";
-        }
-        if ("log".equals(name)) {
-            return bindingVar + ".log()";
-        }
-        if ("this".equals(name)) {
-            return "filterSpec";
-        }
-        if (STATIC_TYPES.contains(name)) {
-            return "org.apache.skywalking.oap.meter.analyzer.dsl.registry.ProcessRegistry";
-        }
-        return name;
-    }
-
-    private String visitProperty(final PropertyExpression expr, final String bindingVar) {
-        final Expression objExpr = expr.getObjectExpression();
-        final String propName = expr.getPropertyAsString();
-        final boolean isSafe = expr.isSafe();
-
-        final String obj = visitValueExpression(objExpr, bindingVar);
-
-        // log.service -> binding.log().getService()
-        if (objExpr instanceof VariableExpression
-                && "log".equals(((VariableExpression) objExpr).getName())) {
-            return visitLogProperty(propName, bindingVar);
-        }
-
-        // For parsed access and nested map access, use getAt()
-        if (isSafe) {
-            return "(" + obj + " == null ? null : getAt(" + obj + ", \"" + propName + "\"))";
-        }
-
-        return "getAt(" + obj + ", \"" + propName + "\")";
-    }
-
-    private String visitLogProperty(final String propName, final String bindingVar) {
-        switch (propName) {
-            case "service":
-                return bindingVar + ".log().getService()";
-            case "serviceInstance":
-                return bindingVar + ".log().getServiceInstance()";
-            case "endpoint":
-                return bindingVar + ".log().getEndpoint()";
-            case "timestamp":
-                return bindingVar + ".log().getTimestamp()";
-            default:
-                return bindingVar + ".log().get" + capitalize(propName) + "()";
-        }
-    }
-
-    private String visitCast(final CastExpression expr, final String bindingVar) {
-        final String inner = visitValueExpression(expr.getExpression(), bindingVar);
-        final String typeName = expr.getType().getName();
-
-        switch (typeName) {
-            case "java.lang.String":
-            case "String":
-                return "String.valueOf(" + inner + ")";
-            case "java.lang.Long":
-            case "Long":
-            case "long":
-                return "toLong(" + inner + ")";
-            case "java.lang.Integer":
-            case "Integer":
-            case "int":
-                return "toInt(" + inner + ")";
-            case "java.lang.Boolean":
-            case "Boolean":
-            case "boolean":
-                return "toBoolean(" + inner + ")";
-            default:
-                return "((" + typeName + ") " + inner + ")";
-        }
-    }
-
-    private String visitMethodCallValue(final MethodCallExpression mce, final String bindingVar) {
-        final String methodName = mce.getMethodAsString();
-        final Expression objExpr = mce.getObjectExpression();
-        final ArgumentListExpression args = toArgList(mce.getArguments());
-        final boolean isSafe = mce.isSafe();
-
-        // tag("KEY") on filterSpec -> filterSpec.tag("KEY")
-        if ("tag".equals(methodName) && isThisOrImplicit(objExpr)) {
-            final List<Expression> argExprs = args.getExpressions();
-            if (!argExprs.isEmpty() && argExprs.get(0) instanceof ConstantExpression) {
-                return "filterSpec.tag(\""
-                    + MalToJavaTranspiler.escapeJava(argExprs.get(0).getText()) + "\")";
-            }
-        }
-
-        // ProcessRegistry.generateVirtualLocalProcess(...)
-        if (objExpr instanceof VariableExpression
-                && STATIC_TYPES.contains(((VariableExpression) objExpr).getName())) {
-            final StringBuilder sb = new StringBuilder();
-            sb.append("org.apache.skywalking.oap.meter.analyzer.dsl.registry.ProcessRegistry.");
-            sb.append(methodName).append("(");
-            final List<Expression> argExprs = args.getExpressions();
-            for (int i = 0; i < argExprs.size(); i++) {
-                if (i > 0) {
-                    sb.append(", ");
-                }
-                sb.append(visitValueExpression(argExprs.get(i), bindingVar));
-            }
-            sb.append(")");
-            return sb.toString();
-        }
-
-        // toString(), trim() on safe navigation chain
-        if ("toString".equals(methodName) || "trim".equals(methodName)) {
-            final String obj = visitValueExpression(objExpr, bindingVar);
-            if (isSafe) {
-                return "(" + obj + " == null ? null : " + obj + "." + methodName + "())";
-            }
-            return obj + "." + methodName + "()";
-        }
-
-        // Generic method call
-        final String obj = visitValueExpression(objExpr, bindingVar);
-        final StringBuilder sb = new StringBuilder();
-        if (isSafe) {
-            sb.append("(").append(obj).append(" == null ? null : ");
-        }
-        sb.append(obj).append(".").append(methodName).append("(");
-        final List<Expression> argExprs = args.getExpressions();
-        for (int i = 0; i < argExprs.size(); i++) {
-            if (i > 0) {
-                sb.append(", ");
-            }
-            sb.append(visitValueExpression(argExprs.get(i), bindingVar));
-        }
-        sb.append(")");
-        if (isSafe) {
-            sb.append(")");
-        }
-        return sb.toString();
-    }
-
-    private String visitGString(final GStringExpression expr, final String bindingVar) {
-        final List<ConstantExpression> strings = expr.getStrings();
-        final List<Expression> values = expr.getValues();
-
-        final StringBuilder sb = new StringBuilder();
-        for (int i = 0; i < strings.size(); i++) {
-            final String text = strings.get(i).getText();
-            if (!text.isEmpty()) {
-                if (sb.length() > 0) {
-                    sb.append(" + ");
-                }
-                sb.append("\"").append(MalToJavaTranspiler.escapeJava(text)).append("\"");
-            }
-            if (i < values.size()) {
-                final String val = visitValueExpression(values.get(i), bindingVar);
-                if (sb.length() > 0) {
-                    sb.append(" + ");
-                }
-                sb.append(val);
-            }
-        }
-        return sb.length() > 0 ? sb.toString() : "\"\"";
-    }
-
-    private String visitMapExpression(final MapExpression expr, final String bindingVar) {
-        final List<MapEntryExpression> entries = expr.getMapEntryExpressions();
-        if (entries.isEmpty()) {
-            return "java.util.Collections.emptyMap()";
-        }
-        if (entries.size() == 1) {
-            final MapEntryExpression e = entries.get(0);
-            return "Map.of(" + visitValueExpression(e.getKeyExpression(), bindingVar)
-                + ", " + visitValueExpression(e.getValueExpression(), bindingVar) + ")";
-        }
-        final StringBuilder sb = new StringBuilder("Map.of(");
-        for (int i = 0; i < entries.size(); i++) {
-            if (i > 0) {
-                sb.append(", ");
-            }
-            final MapEntryExpression e = entries.get(i);
-            sb.append(visitValueExpression(e.getKeyExpression(), bindingVar));
-            sb.append(", ");
-            sb.append(visitValueExpression(e.getValueExpression(), bindingVar));
-        }
-        sb.append(")");
-        return sb.toString();
-    }
-
-    // ---- Helpers ----
-
-    private List<Statement> flattenStatements(final Statement stmt) {
-        if (stmt instanceof BlockStatement) {
-            return ((BlockStatement) stmt).getStatements();
-        }
-        return List.of(stmt);
-    }
-
-    private boolean isThisOrImplicit(final Expression expr) {
-        if (expr instanceof VariableExpression) {
-            final String name = ((VariableExpression) expr).getName();
-            return "this".equals(name);
-        }
-        return false;
-    }
-
-    private String lambdaVarFor(final String methodName) {
-        switch (methodName) {
-            case "extractor": return "ext";
-            case "sink": return "s";
-            case "slowSql": return "sql";
-            case "sampledTrace": return "st";
-            case "metrics": return "m";
-            case "sampler": return "sp";
-            case "text": return "tp";
-            default: return "x";
-        }
-    }
-
-    private String childReceiverFor(final String lambdaVar) {
-        return lambdaVar;
-    }
-
-    private ArgumentListExpression toArgList(final Expression args) {
-        if (args instanceof ArgumentListExpression) {
-            return (ArgumentListExpression) args;
-        }
-        if (args instanceof TupleExpression) {
-            final ArgumentListExpression ale = new ArgumentListExpression();
-            for (Expression e : ((TupleExpression) args).getExpressions()) {
-                ale.addExpression(e);
-            }
-            return ale;
-        }
-        final ArgumentListExpression ale = new ArgumentListExpression();
-        ale.addExpression(args);
-        return ale;
-    }
-
-    private static String capitalize(final String s) {
-        if (s == null || s.isEmpty()) {
-            return s;
-        }
-        return Character.toUpperCase(s.charAt(0)) + s.substring(1);
-    }
-
-    private static void indent(final StringBuilder sb, final int level) {
-        for (int i = 0; i < level; i++) {
-            sb.append("        ");
-        }
-    }
-
-    // ---- Compilation & Manifest ----
-
-    /**
-     * Compile all registered LAL sources using javax.tools.JavaCompiler.
-     */
-    public void compileAll(final File sourceDir, final File outputDir,
-                           final String classpath) throws IOException {
-        if (lalSources.isEmpty()) {
-            log.info("No LAL sources to compile.");
-            return;
-        }
-
-        final String packageDir = GENERATED_PACKAGE.replace('.', File.separatorChar);
-        final File srcPkgDir = new File(sourceDir, packageDir);
-        if (!srcPkgDir.exists() && !srcPkgDir.mkdirs()) {
-            throw new IOException("Failed to create source dir: " + srcPkgDir);
-        }
-        if (!outputDir.exists() && !outputDir.mkdirs()) {
-            throw new IOException("Failed to create output dir: " + outputDir);
-        }
-
-        final List<File> javaFiles = new ArrayList<>();
-        for (Map.Entry<String, String> entry : lalSources.entrySet()) {
-            final File javaFile = new File(srcPkgDir, entry.getKey() + ".java");
-            Files.writeString(javaFile.toPath(), entry.getValue());
-            javaFiles.add(javaFile);
-        }
-
-        final JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
-        if (compiler == null) {
-            throw new IllegalStateException("No Java compiler available — requires JDK");
-        }
-
-        final StringWriter errorWriter = new StringWriter();
-
-        try (StandardJavaFileManager fileManager = compiler.getStandardFileManager(null, null, null)) {
-            final Iterable<? extends JavaFileObject> compilationUnits =
-                fileManager.getJavaFileObjectsFromFiles(javaFiles);
-
-            final List<String> options = Arrays.asList(
-                "-d", outputDir.getAbsolutePath(),
-                "-classpath", classpath
-            );
-
-            final JavaCompiler.CompilationTask task = compiler.getTask(
-                errorWriter, fileManager, null, options, null, compilationUnits);
-
-            final boolean success = task.call();
-            if (!success) {
-                for (Map.Entry<String, String> entry : lalSources.entrySet()) {
-                    log.error("Generated source for {}:\n{}", entry.getKey(), entry.getValue());
-                }
-                throw new RuntimeException(
-                    "Java compilation failed for " + javaFiles.size() + " LAL sources:\n"
-                        + errorWriter);
-            }
-        }
-
-        log.info("Compiled {} LAL sources to {}", lalSources.size(), outputDir);
-    }
-
-    /**
-     * Write lal-expressions.txt manifest: hash=FQCN format.
-     */
-    public void writeManifest(final File outputDir) throws IOException {
-        final File manifestDir = new File(outputDir, "META-INF");
-        if (!manifestDir.exists() && !manifestDir.mkdirs()) {
-            throw new IOException("Failed to create META-INF dir: " + manifestDir);
-        }
-
-        final List<String> lines = hashToClass.entrySet().stream()
-            .map(e -> e.getKey() + "=" + e.getValue())
-            .sorted()
-            .collect(Collectors.toList());
-        Files.write(new File(manifestDir, "lal-expressions.txt").toPath(), lines);
-        log.info("Wrote lal-expressions.txt with {} entries", lines.size());
-    }
-}
diff --git a/oap-server/analyzer/lal-transpiler/src/test/java/org/apache/skywalking/oap/server/transpiler/lal/LalToJavaTranspilerTest.java b/oap-server/analyzer/lal-transpiler/src/test/java/org/apache/skywalking/oap/server/transpiler/lal/LalToJavaTranspilerTest.java
deleted file mode 100644
index 4a580a4bcd..0000000000
--- a/oap-server/analyzer/lal-transpiler/src/test/java/org/apache/skywalking/oap/server/transpiler/lal/LalToJavaTranspilerTest.java
+++ /dev/null
@@ -1,678 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.skywalking.oap.server.transpiler.lal;
-
-import java.io.File;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.util.List;
-
-import org.junit.jupiter.api.BeforeEach;
-import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.io.TempDir;
-
-import static org.junit.jupiter.api.Assertions.assertNotNull;
-import static org.junit.jupiter.api.Assertions.assertTrue;
-
-class LalToJavaTranspilerTest {
-
-    private LalToJavaTranspiler transpiler;
-
-    @BeforeEach
-    void setUp() {
-        transpiler = new LalToJavaTranspiler();
-    }
-
-    // ---- Class Structure ----
-
-    @Test
-    void classStructure() {
-        final String java = transpiler.transpile("LalExpr_test",
-            "filter { json {} }");
-        assertNotNull(java);
-
-        assertTrue(java.contains("package " + LalToJavaTranspiler.GENERATED_PACKAGE),
-            "Should have correct package");
-        assertTrue(java.contains("public class LalExpr_test implements LalExpression"),
-            "Should implement LalExpression");
-        assertTrue(java.contains("public void execute(FilterSpec filterSpec, Binding binding)"),
-            "Should have execute method");
-    }
-
-    @Test
-    void helperMethodsPresent() {
-        final String java = transpiler.transpile("LalExpr_test",
-            "filter { json {} }");
-        assertNotNull(java);
-
-        assertTrue(java.contains("private static Object getAt(Object obj, String key)"),
-            "Should have getAt helper");
-        assertTrue(java.contains("private static long toLong(Object obj)"),
-            "Should have toLong helper");
-        assertTrue(java.contains("private static int toInt(Object obj)"),
-            "Should have toInt helper");
-        assertTrue(java.contains("private static boolean toBoolean(Object obj)"),
-            "Should have toBoolean helper");
-        assertTrue(java.contains("private static boolean isTruthy(Object obj)"),
-            "Should have isTruthy helper");
-        assertTrue(java.contains("private static boolean isNonEmptyString(Object obj)"),
-            "Should have isNonEmptyString helper");
-    }
-
-    @Test
-    void importsPresent() {
-        final String java = transpiler.transpile("LalExpr_test",
-            "filter { json {} }");
-        assertNotNull(java);
-
-        assertTrue(java.contains("import org.apache.skywalking.oap.log.analyzer.dsl.Binding;"),
-            "Should import Binding");
-        assertTrue(java.contains("import org.apache.skywalking.oap.log.analyzer.dsl.LalExpression;"),
-            "Should import LalExpression");
-        assertTrue(java.contains("import org.apache.skywalking.oap.log.analyzer.dsl.spec.filter.FilterSpec;"),
-            "Should import FilterSpec");
-    }
-
-    // ---- filter {} Unwrapping ----
-
-    @Test
-    void filterUnwrap() {
-        final String java = transpiler.transpile("LalExpr_test",
-            "filter { json {} }");
-        assertNotNull(java);
-
-        assertTrue(java.contains("filterSpec.json()"),
-            "Should unwrap filter block and call json() on filterSpec");
-        assertTrue(!java.contains("filterSpec.filter("),
-            "Should NOT emit filterSpec.filter() call");
-    }
-
-    // ---- json {} ----
-
-    @Test
-    void jsonEmptyBlock() {
-        final String java = transpiler.transpile("LalExpr_test",
-            "filter { json {} }");
-        assertNotNull(java);
-
-        assertTrue(java.contains("filterSpec.json();"),
-            "Should emit no-arg json() call");
-    }
-
-    // ---- text { regexp $/pattern/$ } ----
-
-    @Test
-    void textWithRegexp() {
-        final String java = transpiler.transpile("LalExpr_test",
-            "filter { text { regexp $/(?<timestamp>\\d+)\\s+(?<service>.+)/$ } }");
-        assertNotNull(java);
-
-        assertTrue(java.contains("filterSpec.text(tp -> {"),
-            "Should emit text with Consumer lambda");
-        assertTrue(java.contains("tp.regexp("),
-            "Should call regexp on text parser spec");
-    }
-
-    // ---- extractor { ... } ----
-
-    @Test
-    void extractorWithService() {
-        final String java = transpiler.transpile("LalExpr_test",
-            "filter {\n" +
-            "  json {}\n" +
-            "  extractor {\n" +
-            "    service parsed.service as String\n" +
-            "  }\n" +
-            "}");
-        assertNotNull(java);
-
-        assertTrue(java.contains("filterSpec.extractor(ext -> {"),
-            "Should emit extractor with Consumer lambda");
-        assertTrue(java.contains("ext.service("),
-            "Should call service on extractor spec");
-        assertTrue(java.contains("String.valueOf("),
-            "Should use String.valueOf for 'as String' cast");
-        assertTrue(java.contains("getAt(binding.parsed(), \"service\")"),
-            "Should use getAt for parsed.service");
-    }
-
-    @Test
-    void extractorWithMultipleFields() {
-        final String java = transpiler.transpile("LalExpr_test",
-            "filter {\n" +
-            "  json {}\n" +
-            "  extractor {\n" +
-            "    service parsed.service as String\n" +
-            "    instance parsed.instance as String\n" +
-            "    endpoint parsed.endpoint as String\n" +
-            "    layer parsed.layer as String\n" +
-            "    timestamp parsed.time as String\n" +
-            "    traceId parsed.traceId as String\n" +
-            "  }\n" +
-            "}");
-        assertNotNull(java);
-
-        assertTrue(java.contains("ext.service(String.valueOf(getAt(binding.parsed(), \"service\")))"),
-            "Should extract service");
-        assertTrue(java.contains("ext.instance(String.valueOf(getAt(binding.parsed(), \"instance\")))"),
-            "Should extract instance");
-        assertTrue(java.contains("ext.endpoint(String.valueOf(getAt(binding.parsed(), \"endpoint\")))"),
-            "Should extract endpoint");
-        assertTrue(java.contains("ext.layer(String.valueOf(getAt(binding.parsed(), \"layer\")))"),
-            "Should extract layer");
-        assertTrue(java.contains("ext.timestamp(String.valueOf(getAt(binding.parsed(), \"time\")))"),
-            "Should extract timestamp");
-        assertTrue(java.contains("ext.traceId(String.valueOf(getAt(binding.parsed(), \"traceId\")))"),
-            "Should extract traceId");
-    }
-
-    // ---- sink { ... } ----
-
-    @Test
-    void sinkWithEnforcer() {
-        final String java = transpiler.transpile("LalExpr_test",
-            "filter {\n" +
-            "  json {}\n" +
-            "  sink {\n" +
-            "    enforcer {}\n" +
-            "  }\n" +
-            "}");
-        assertNotNull(java);
-
-        assertTrue(java.contains("filterSpec.sink(s -> {"),
-            "Should emit sink with Consumer lambda");
-        assertTrue(java.contains("s.enforcer();"),
-            "Should emit no-arg enforcer() on sink spec");
-    }
-
-    @Test
-    void sinkWithDropper() {
-        final String java = transpiler.transpile("LalExpr_test",
-            "filter {\n" +
-            "  json {}\n" +
-            "  sink {\n" +
-            "    dropper {}\n" +
-            "  }\n" +
-            "}");
-        assertNotNull(java);
-
-        assertTrue(java.contains("s.dropper();"),
-            "Should emit no-arg dropper() on sink spec");
-    }
-
-    @Test
-    void sinkWithSampler() {
-        final String java = transpiler.transpile("LalExpr_test",
-            "filter {\n" +
-            "  json {}\n" +
-            "  sink {\n" +
-            "    sampler {\n" +
-            "      rateLimit('abc')\n" +
-            "    }\n" +
-            "  }\n" +
-            "}");
-        assertNotNull(java);
-
-        assertTrue(java.contains("s.sampler(sp -> {"),
-            "Should emit sampler with Consumer lambda");
-        assertTrue(java.contains("sp.rateLimit(\"abc\")"),
-            "Should call rateLimit on sampler spec");
-    }
-
-    // ---- abort {} ----
-
-    @Test
-    void abortBlock() {
-        final String java = transpiler.transpile("LalExpr_test",
-            "filter {\n" +
-            "  json {}\n" +
-            "  abort {}\n" +
-            "}");
-        assertNotNull(java);
-
-        assertTrue(java.contains("filterSpec.abort();"),
-            "Should emit no-arg abort() call");
-    }
-
-    // ---- parsed access ----
-
-    @Test
-    void parsedPropertyAccess() {
-        final String java = transpiler.transpile("LalExpr_test",
-            "filter {\n" +
-            "  json {}\n" +
-            "  extractor { service parsed.service as String }\n" +
-            "}");
-        assertNotNull(java);
-
-        assertTrue(java.contains("binding.parsed()"),
-            "Should translate 'parsed' to binding.parsed()");
-        assertTrue(java.contains("getAt(binding.parsed(), \"service\")"),
-            "Should use getAt for property access");
-    }
-
-    @Test
-    void parsedNestedAccess() {
-        final String java = transpiler.transpile("LalExpr_test",
-            "filter {\n" +
-            "  json {}\n" +
-            "  extractor { service parsed.data.serviceName as String }\n" +
-            "}");
-        assertNotNull(java);
-
-        assertTrue(java.contains("getAt(getAt(binding.parsed(), \"data\"), \"serviceName\")"),
-            "Should translate nested parsed access to nested getAt()");
-    }
-
-    // ---- Safe navigation (?.) ----
-
-    @Test
-    void safeNavigationOnParsed() {
-        final String java = transpiler.transpile("LalExpr_test",
-            "filter {\n" +
-            "  json {}\n" +
-            "  extractor { service parsed?.service as String }\n" +
-            "}");
-        assertNotNull(java);
-
-        assertTrue(java.contains("== null ? null : getAt("),
-            "Should translate ?. to null-safe ternary with getAt");
-    }
-
-    // ---- as Cast ----
-
-    @Test
-    void castAsString() {
-        final String java = transpiler.transpile("LalExpr_test",
-            "filter {\n" +
-            "  json {}\n" +
-            "  extractor { service parsed.service as String }\n" +
-            "}");
-        assertNotNull(java);
-
-        assertTrue(java.contains("String.valueOf("),
-            "Should translate 'as String' to String.valueOf()");
-    }
-
-    @Test
-    void castAsLong() {
-        final String java = transpiler.transpile("LalExpr_test",
-            "filter {\n" +
-            "  json {}\n" +
-            "  extractor { timestamp parsed.time as Long }\n" +
-            "}");
-        assertNotNull(java);
-
-        assertTrue(java.contains("toLong("),
-            "Should translate 'as Long' to toLong()");
-    }
-
-    // ---- log access ----
-
-    @Test
-    void logPropertyAccess() {
-        final String java = transpiler.transpile("LalExpr_test",
-            "filter {\n" +
-            "  json {}\n" +
-            "  extractor { service log.service }\n" +
-            "}");
-        assertNotNull(java);
-
-        assertTrue(java.contains("binding.log().getService()"),
-            "Should translate log.service to binding.log().getService()");
-    }
-
-    // ---- if/else ----
-
-    @Test
-    void ifStatement() {
-        final String java = transpiler.transpile("LalExpr_test",
-            "filter {\n" +
-            "  json {}\n" +
-            "  if (parsed.level == 'ERROR') {\n" +
-            "    abort {}\n" +
-            "  }\n" +
-            "}");
-        assertNotNull(java);
-
-        assertTrue(java.contains("if (\"ERROR\".equals(getAt(binding.parsed(), \"level\"))"),
-            "Should translate == with constant on left for null-safety");
-        assertTrue(java.contains("filterSpec.abort()"),
-            "Should emit abort in if body");
-    }
-
-    @Test
-    void ifElseStatement() {
-        final String java = transpiler.transpile("LalExpr_test",
-            "filter {\n" +
-            "  json {}\n" +
-            "  if (parsed.type == 'access') {\n" +
-            "    extractor { layer 'HTTP' }\n" +
-            "  } else {\n" +
-            "    extractor { layer 'GENERAL' }\n" +
-            "  }\n" +
-            "}");
-        assertNotNull(java);
-
-        assertTrue(java.contains("if (\"access\".equals(getAt(binding.parsed(), \"type\"))"),
-            "Should have if condition");
-        assertTrue(java.contains("} else {"),
-            "Should have else block");
-    }
-
-    @Test
-    void ifElseIfStatement() {
-        final String java = transpiler.transpile("LalExpr_test",
-            "filter {\n" +
-            "  json {}\n" +
-            "  if (parsed.type == 'a') {\n" +
-            "    extractor { layer 'A' }\n" +
-            "  } else if (parsed.type == 'b') {\n" +
-            "    extractor { layer 'B' }\n" +
-            "  } else {\n" +
-            "    extractor { layer 'C' }\n" +
-            "  }\n" +
-            "}");
-        assertNotNull(java);
-
-        assertTrue(java.contains("} else if ("),
-            "Should produce else-if chain");
-    }
-
-    // ---- Condition operators ----
-
-    @Test
-    void notEqualCondition() {
-        final String java = transpiler.transpile("LalExpr_test",
-            "filter {\n" +
-            "  json {}\n" +
-            "  if (parsed.status != 'ok') { abort {} }\n" +
-            "}");
-        assertNotNull(java);
-
-        assertTrue(java.contains("!\"ok\".equals(getAt(binding.parsed(), \"status\"))"),
-            "Should translate != with negated .equals()");
-    }
-
-    @Test
-    void logicalAndCondition() {
-        final String java = transpiler.transpile("LalExpr_test",
-            "filter {\n" +
-            "  json {}\n" +
-            "  if (parsed.a == 'x' && parsed.b == 'y') { abort {} }\n" +
-            "}");
-        assertNotNull(java);
-
-        assertTrue(java.contains("\"x\".equals(getAt(binding.parsed(), \"a\")) && \"y\".equals(getAt(binding.parsed(), \"b\"))"),
-            "Should translate && with .equals() on both sides");
-    }
-
-    @Test
-    void logicalOrCondition() {
-        final String java = transpiler.transpile("LalExpr_test",
-            "filter {\n" +
-            "  json {}\n" +
-            "  if (parsed.a == 'x' || parsed.a == 'y') { abort {} }\n" +
-            "}");
-        assertNotNull(java);
-
-        assertTrue(java.contains("\"x\".equals(") && java.contains("|| \"y\".equals("),
-            "Should translate || correctly");
-    }
-
-    @Test
-    void truthinessCondition() {
-        final String java = transpiler.transpile("LalExpr_test",
-            "filter {\n" +
-            "  json {}\n" +
-            "  if (parsed.value) { abort {} }\n" +
-            "}");
-        assertNotNull(java);
-
-        assertTrue(java.contains("isTruthy(getAt(binding.parsed(), \"value\"))"),
-            "Should translate bare expression to isTruthy()");
-    }
-
-    @Test
-    void negationCondition() {
-        final String java = transpiler.transpile("LalExpr_test",
-            "filter {\n" +
-            "  json {}\n" +
-            "  if (!parsed.value) { abort {} }\n" +
-            "}");
-        assertNotNull(java);
-
-        assertTrue(java.contains("!isTruthy(getAt(binding.parsed(), \"value\"))"),
-            "Should translate !expr to negated isTruthy()");
-    }
-
-    @Test
-    void lessThanCondition() {
-        final String java = transpiler.transpile("LalExpr_test",
-            "filter {\n" +
-            "  json {}\n" +
-            "  if (parsed.code < 400) { abort {} }\n" +
-            "}");
-        assertNotNull(java);
-
-        assertTrue(java.contains("toInt(getAt(binding.parsed(), \"code\")) < 400"),
-            "Should translate < with toInt on left");
-    }
-
-    @Test
-    void greaterThanEqualCondition() {
-        final String java = transpiler.transpile("LalExpr_test",
-            "filter {\n" +
-            "  json {}\n" +
-            "  if (parsed.code >= 500) { abort {} }\n" +
-            "}");
-        assertNotNull(java);
-
-        assertTrue(java.contains("toInt(getAt(binding.parsed(), \"code\")) >= 500"),
-            "Should translate >= with toInt on left");
-    }
-
-    // ---- GString interpolation ----
-
-    @Test
-    void gstringInterpolation() {
-        final String java = transpiler.transpile("LalExpr_test",
-            "filter {\n" +
-            "  json {}\n" +
-            "  extractor { service \"svc::${parsed.name}\" as String }\n" +
-            "}");
-        assertNotNull(java);
-
-        assertTrue(java.contains("\"svc::\""),
-            "Should have string prefix");
-        assertTrue(java.contains("getAt(binding.parsed(), \"name\")"),
-            "Should have parsed access in interpolation");
-    }
-
-    // ---- tag(Map) ----
-
-    @Test
-    void tagWithMapLiteral() {
-        final String java = transpiler.transpile("LalExpr_test",
-            "filter {\n" +
-            "  json {}\n" +
-            "  extractor {\n" +
-            "    tag(status: parsed.status as String)\n" +
-            "  }\n" +
-            "}");
-        assertNotNull(java);
-
-        assertTrue(java.contains("ext.tag("),
-            "Should call tag on extractor spec");
-    }
-
-    // ---- slowSql / sampledTrace / metrics ----
-
-    @Test
-    void slowSqlBlock() {
-        final String java = transpiler.transpile("LalExpr_test",
-            "filter {\n" +
-            "  json {}\n" +
-            "  extractor {\n" +
-            "    layer 'MYSQL'\n" +
-            "    service parsed.service as String\n" +
-            "    slowSql {\n" +
-            "      id parsed.id as String\n" +
-            "      statement parsed.statement as String\n" +
-            "      latency parsed.latency as Long\n" +
-            "    }\n" +
-            "  }\n" +
-            "}");
-        assertNotNull(java);
-
-        assertTrue(java.contains("ext.slowSql(sql -> {"),
-            "Should emit slowSql with Consumer lambda, var 'sql'");
-        assertTrue(java.contains("sql.id("),
-            "Should call id on slowSql spec");
-        assertTrue(java.contains("sql.statement("),
-            "Should call statement on slowSql spec");
-        assertTrue(java.contains("sql.latency(toLong("),
-            "Should call latency with toLong");
-    }
-
-    @Test
-    void sampledTraceBlock() {
-        final String java = transpiler.transpile("LalExpr_test",
-            "filter {\n" +
-            "  json {}\n" +
-            "  extractor {\n" +
-            "    sampledTrace {\n" +
-            "      uri parsed.uri as String\n" +
-            "      latency parsed.latency as Long\n" +
-            "    }\n" +
-            "  }\n" +
-            "}");
-        assertNotNull(java);
-
-        assertTrue(java.contains("ext.sampledTrace(st -> {"),
-            "Should emit sampledTrace with Consumer lambda, var 'st'");
-        assertTrue(java.contains("st.uri("),
-            "Should call uri on sampledTrace spec");
-    }
-
-    @Test
-    void metricsBlock() {
-        final String java = transpiler.transpile("LalExpr_test",
-            "filter {\n" +
-            "  json {}\n" +
-            "  extractor {\n" +
-            "    metrics {\n" +
-            "      name 'log_count'\n" +
-            "      value 1\n" +
-            "    }\n" +
-            "  }\n" +
-            "}");
-        assertNotNull(java);
-
-        assertTrue(java.contains("ext.metrics(m -> {"),
-            "Should emit metrics with Consumer lambda, var 'm'");
-        assertTrue(java.contains("m.name(\"log_count\")"),
-            "Should call name on metrics spec");
-        assertTrue(java.contains("m.value(1)"),
-            "Should call value on metrics spec");
-    }
-
-    // ---- Complete LAL Script ----
-
-    @Test
-    void completeScript() {
-        final String java = transpiler.transpile("LalExpr_test",
-            "filter {\n" +
-            "  json {}\n" +
-            "  extractor {\n" +
-            "    service parsed.service as String\n" +
-            "    instance parsed.instance as String\n" +
-            "    layer parsed.layer as String\n" +
-            "    timestamp parsed.time as String\n" +
-            "  }\n" +
-            "  sink {\n" +
-            "    enforcer {}\n" +
-            "  }\n" +
-            "}");
-        assertNotNull(java);
-
-        assertTrue(java.contains("filterSpec.json();"),
-            "Should have json() call");
-        assertTrue(java.contains("filterSpec.extractor(ext -> {"),
-            "Should have extractor block");
-        assertTrue(java.contains("filterSpec.sink(s -> {"),
-            "Should have sink block");
-        assertTrue(java.contains("s.enforcer();"),
-            "Should have enforcer in sink");
-    }
-
-    // ---- tag("KEY") as value ----
-
-    @Test
-    void tagAsValue() {
-        final String java = transpiler.transpile("LalExpr_test",
-            "filter {\n" +
-            "  json {}\n" +
-            "  if (tag('status') == 'error') { abort {} }\n" +
-            "}");
-        assertNotNull(java);
-
-        assertTrue(java.contains("filterSpec.tag(\"status\")"),
-            "Should call tag on filterSpec");
-    }
-
-    // ---- rateLimit ----
-
-    @Test
-    void rateLimitWithClosureArg() {
-        final String java = transpiler.transpile("LalExpr_test",
-            "filter {\n" +
-            "  json {}\n" +
-            "  sink {\n" +
-            "    sampler {\n" +
-            "      rateLimit('myId') { rpm 5 }\n" +
-            "    }\n" +
-            "  }\n" +
-            "}");
-        assertNotNull(java);
-
-        assertTrue(java.contains("sp.rateLimit(\"myId\", rls -> {"),
-            "Should emit rateLimit with id and closure lambda");
-        assertTrue(java.contains("rls.rpm(5)"),
-            "Should call rpm on rate limit spec");
-    }
-
-    // ---- Manifest ----
-
-    @Test
-    void manifest(@TempDir Path tempDir) throws Exception {
-        final String source = transpiler.transpile("LalExpr_a", "filter { json {} }");
-        transpiler.register("LalExpr_a", "abc123hash", source);
-
-        final File outputDir = tempDir.toFile();
-        transpiler.writeManifest(outputDir);
-
-        final File manifest = new File(outputDir, "META-INF/lal-expressions.txt");
-        assertTrue(manifest.exists(), "Manifest file should exist");
-
-        final List<String> lines = Files.readAllLines(manifest.toPath());
-        assertTrue(lines.stream().anyMatch(l -> l.contains("abc123hash") &&
-            l.contains(LalToJavaTranspiler.GENERATED_PACKAGE + ".LalExpr_a")),
-            "Should contain hash=FQCN mapping");
-    }
-}
diff --git a/oap-server/analyzer/log-analyzer-v2/pom.xml b/oap-server/analyzer/log-analyzer-v2/pom.xml
deleted file mode 100644
index 6456f8209f..0000000000
--- a/oap-server/analyzer/log-analyzer-v2/pom.xml
+++ /dev/null
@@ -1,72 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-  ~ Licensed to the Apache Software Foundation (ASF) under one or more
-  ~ contributor license agreements.  See the NOTICE file distributed with
-  ~ this work for additional information regarding copyright ownership.
-  ~ The ASF licenses this file to You under the Apache License, Version 2.0
-  ~ (the "License"); you may not use this file except in compliance with
-  ~ the License.  You may obtain a copy of the License at
-  ~
-  ~     http://www.apache.org/licenses/LICENSE-2.0
-  ~
-  ~ Unless required by applicable law or agreed to in writing, software
-  ~ distributed under the License is distributed on an "AS IS" BASIS,
-  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  ~ See the License for the specific language governing permissions and
-  ~ limitations under the License.
-  ~
-  -->
-
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
-    <parent>
-        <artifactId>analyzer</artifactId>
-        <groupId>org.apache.skywalking</groupId>
-        <version>${revision}</version>
-    </parent>
-    <modelVersion>4.0.0</modelVersion>
-
-    <artifactId>log-analyzer-v2</artifactId>
-    <description>Pure Java LAL runtime that loads transpiled LalExpression classes from manifest instead of Groovy</description>
-
-    <dependencies>
-        <dependency>
-            <groupId>org.apache.skywalking</groupId>
-            <artifactId>log-analyzer</artifactId>
-            <version>${project.version}</version>
-        </dependency>
-    </dependencies>
-
-    <build>
-        <plugins>
-            <plugin>
-                <groupId>org.apache.maven.plugins</groupId>
-                <artifactId>maven-shade-plugin</artifactId>
-                <executions>
-                    <execution>
-                        <phase>package</phase>
-                        <goals>
-                            <goal>shade</goal>
-                        </goals>
-                        <configuration>
-                            <createDependencyReducedPom>true</createDependencyReducedPom>
-                            <artifactSet>
-                                <includes>
-                                    <include>org.apache.skywalking:log-analyzer</include>
-                                </includes>
-                            </artifactSet>
-                            <filters>
-                                <filter>
-                                    <artifact>org.apache.skywalking:log-analyzer</artifact>
-                                    <excludes>
-                                        <exclude>org/apache/skywalking/oap/log/analyzer/dsl/DSL.class</exclude>
-                                        <exclude>org/apache/skywalking/oap/log/analyzer/dsl/DSL$*.class</exclude>
-                                    </excludes>
-                                </filter>
-                            </filters>
-                        </configuration>
-                    </execution>
-                </executions>
-            </plugin>
-        </plugins>
-    </build>
-</project>
diff --git a/oap-server/analyzer/log-analyzer-v2/src/main/java/org/apache/skywalking/oap/log/analyzer/dsl/DSL.java b/oap-server/analyzer/log-analyzer-v2/src/main/java/org/apache/skywalking/oap/log/analyzer/dsl/DSL.java
deleted file mode 100644
index b3f4e4c977..0000000000
--- a/oap-server/analyzer/log-analyzer-v2/src/main/java/org/apache/skywalking/oap/log/analyzer/dsl/DSL.java
+++ /dev/null
@@ -1,142 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.skywalking.oap.log.analyzer.dsl;
-
-import java.io.BufferedReader;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.nio.charset.StandardCharsets;
-import java.security.MessageDigest;
-import java.security.NoSuchAlgorithmException;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.concurrent.atomic.AtomicInteger;
-import lombok.AccessLevel;
-import lombok.RequiredArgsConstructor;
-import lombok.extern.slf4j.Slf4j;
-import org.apache.skywalking.oap.log.analyzer.dsl.spec.filter.FilterSpec;
-import org.apache.skywalking.oap.log.analyzer.provider.LogAnalyzerModuleConfig;
-import org.apache.skywalking.oap.server.library.module.ModuleManager;
-import org.apache.skywalking.oap.server.library.module.ModuleStartException;
-
-/**
- * Same-FQCN replacement for upstream LAL DSL.
- * Loads pre-compiled {@link LalExpression} classes from lal-expressions.txt manifest
- * (keyed by SHA-256 hash) instead of Groovy {@code GroovyShell} runtime compilation.
- */
-@Slf4j
-@RequiredArgsConstructor(access = AccessLevel.PRIVATE)
-public class DSL {
-    private static final String MANIFEST_PATH = "META-INF/lal-expressions.txt";
-    private static volatile Map<String, String> EXPRESSION_MAP;
-    private static final AtomicInteger LOADED_COUNT = new AtomicInteger();
-
-    private final LalExpression expression;
-    private final FilterSpec filterSpec;
-    private Binding binding;
-
-    public static DSL of(final ModuleManager moduleManager,
-                         final LogAnalyzerModuleConfig config,
-                         final String dsl) throws ModuleStartException {
-        final Map<String, String> exprMap = loadManifest();
-        final String dslHash = sha256(dsl);
-        final String className = exprMap.get(dslHash);
-        if (className == null) {
-            throw new ModuleStartException(
-                "Pre-compiled LAL expression not found for DSL hash: " + dslHash
-                    + ". Available: " + exprMap.size() + " expressions.");
-        }
-
-        try {
-            final Class<?> exprClass = Class.forName(className);
-            final LalExpression expression = (LalExpression) exprClass.getDeclaredConstructor().newInstance();
-            final FilterSpec filterSpec = new FilterSpec(moduleManager, config);
-            final int count = LOADED_COUNT.incrementAndGet();
-            log.debug("Loaded pre-compiled LAL expression [{}/{}]: {}", count, exprMap.size(), className);
-            return new DSL(expression, filterSpec);
-        } catch (ClassNotFoundException e) {
-            throw new ModuleStartException(
-                "Pre-compiled LAL expression class not found: " + className, e);
-        } catch (ReflectiveOperationException e) {
-            throw new ModuleStartException(
-                "Pre-compiled LAL expression instantiation failed: " + className, e);
-        }
-    }
-
-    public void bind(final Binding binding) {
-        this.binding = binding;
-        this.filterSpec.bind(binding);
-    }
-
-    public void evaluate() {
-        expression.execute(filterSpec, binding);
-    }
-
-    private static Map<String, String> loadManifest() {
-        if (EXPRESSION_MAP != null) {
-            return EXPRESSION_MAP;
-        }
-        synchronized (DSL.class) {
-            if (EXPRESSION_MAP != null) {
-                return EXPRESSION_MAP;
-            }
-            final Map<String, String> map = new HashMap<>();
-            try (InputStream is = DSL.class.getClassLoader().getResourceAsStream(MANIFEST_PATH)) {
-                if (is == null) {
-                    log.warn("LAL expression manifest not found: {}", MANIFEST_PATH);
-                    EXPRESSION_MAP = map;
-                    return map;
-                }
-                try (BufferedReader reader = new BufferedReader(
-                    new InputStreamReader(is, StandardCharsets.UTF_8))) {
-                    String line;
-                    while ((line = reader.readLine()) != null) {
-                        line = line.trim();
-                        if (line.isEmpty()) {
-                            continue;
-                        }
-                        final String[] parts = line.split("=", 2);
-                        if (parts.length == 2) {
-                            map.put(parts[0], parts[1]);
-                        }
-                    }
-                }
-            } catch (IOException e) {
-                throw new IllegalStateException("Failed to load LAL expression manifest", e);
-            }
-            log.info("Loaded {} pre-compiled LAL expressions from manifest", map.size());
-            EXPRESSION_MAP = map;
-            return map;
-        }
-    }
-
-    static String sha256(final String input) {
-        try {
-            final MessageDigest digest = MessageDigest.getInstance("SHA-256");
-            final byte[] hash = digest.digest(input.getBytes(StandardCharsets.UTF_8));
-            final StringBuilder hex = new StringBuilder();
-            for (final byte b : hash) {
-                hex.append(String.format("%02x", b));
-            }
-            return hex.toString();
-        } catch (NoSuchAlgorithmException e) {
-            throw new IllegalStateException("SHA-256 not available", e);
-        }
-    }
-}
diff --git a/oap-server/analyzer/log-analyzer/CLAUDE.md b/oap-server/analyzer/log-analyzer/CLAUDE.md
new file mode 100644
index 0000000000..a7b60c4a8e
--- /dev/null
+++ b/oap-server/analyzer/log-analyzer/CLAUDE.md
@@ -0,0 +1,128 @@
+# LAL Compiler
+
+Compiles LAL (Log Analysis Language) scripts into `LalExpression` implementation classes at runtime using ANTLR4 parsing and Javassist bytecode generation.
+
+## Compilation Workflow
+
+```
+LAL DSL string
+  → LALScriptParser.parse(dsl)                 [ANTLR4 lexer/parser → listener]
+  → LALScriptModel (immutable AST)
+  → LALClassGenerator.compileFromModel(model)
+      Phase 1: collectConsumers(model)   — pre-scan for blocks needing Consumer callbacks
+      Phase 2: compileConsumerClass()    — generate each consumer as separate Javassist class
+      Phase 3: classPool.makeClass()     — create main class implementing LalExpression
+      Phase 4: generateExecuteMethod()   — emit Java source referencing this._consumerN fields
+      Phase 5: ctClass.toClass(LalExpressionPackageHolder.class) + wire consumer fields
+  → LalExpression instance
+```
+
+The generated class implements:
+```java
+void execute(Object filterSpec, Object binding)
+  // cast internally to FilterSpec and Binding
+```
+
+## File Structure
+
+```
+oap-server/analyzer/log-analyzer/
+  src/main/antlr4/.../LALLexer.g4       — ANTLR4 lexer grammar
+  src/main/antlr4/.../LALParser.g4      — ANTLR4 parser grammar
+
+  src/main/java/.../compiler/
+    LALScriptParser.java                — ANTLR4 facade: DSL string → AST
+    LALScriptModel.java                 — Immutable AST model classes
+    LALClassGenerator.java              — Javassist code generator
+    rt/
+      LalExpressionPackageHolder.java   — Class loading anchor (empty marker)
+      BindingAware.java                 — Interface for consumers needing Binding access
+
+  src/test/java/.../compiler/
+    LALScriptParserTest.java            — 8 parser tests
+    LALClassGeneratorTest.java          — 6 generator tests
+```
+
+## Package & Class Naming
+
+| Component | Package / Name |
+|-----------|---------------|
+| Parser/Model/Generator | `org.apache.skywalking.oap.log.analyzer.compiler` |
+| Generated classes | `org.apache.skywalking.oap.log.analyzer.compiler.rt.LalExpr_<N>` |
+| Consumer classes | `org.apache.skywalking.oap.log.analyzer.compiler.rt.LalExpr_<N>_C<M>` |
+| Package holder | `org.apache.skywalking.oap.log.analyzer.compiler.rt.LalExpressionPackageHolder` |
+| Binding aware | `org.apache.skywalking.oap.log.analyzer.compiler.rt.BindingAware` |
+| Functional interface | `org.apache.skywalking.oap.log.analyzer.dsl.LalExpression` (in log-analyzer) |
+
+`<N>` is a global `AtomicInteger` counter. `<M>` is the consumer index within the script.
+
+## Consumer Pattern (BindingAware)
+
+LAL's FilterSpec API uses `Consumer` callbacks: `filterSpec.extractor(Consumer<ExtractorSpec>)`, `filterSpec.sink(Consumer<SinkSpec>)`, etc. Since Javassist cannot compile anonymous inner classes, consumers are pre-compiled as separate classes.
+
+Each consumer class implements both `java.util.function.Consumer` and `BindingAware`:
+- `BindingAware.setBinding(Binding)` — called before each FilterSpec method to inject the current Binding
+- `Consumer.accept(Object)` — casts to the specific Spec type and executes the block body
+
+The main class's `execute()` method emits:
+```java
+((BindingAware) this._consumer0).setBinding(binding);
+filterSpec.extractor(this._consumer0);
+```
+
+Consumer traversal order in `collectConsumers()` must exactly match the order in `generateFilterStatement()`.
+
+## Javassist Constraints
+
+- **No anonymous inner classes**: All `Consumer` callbacks pre-compiled as separate `CtClass` instances.
+- **No lambda expressions**: Same workaround as above.
+- **Spec class packages**: Parser specs are in `dsl.spec.parser.*` (not `dsl.spec.extractor.*`):
+  - `spec.parser.TextParserSpec`, `spec.parser.JsonParserSpec`, `spec.parser.YamlParserSpec`
+  - `spec.extractor.ExtractorSpec`
+  - `spec.extractor.slowsql.SlowSqlSpec`, `spec.extractor.sampledtrace.SampledTraceSpec`
+  - `spec.sink.SinkSpec`, `spec.sink.SamplerSpec`
+
+## Example
+
+**Input**: `filter { json {} extractor { service parsed.service as String } sink {} }`
+
+Three classes are generated:
+
+1. **`LalExpr_0_C0`** — Consumer for extractor block:
+   ```java
+   // implements Consumer, BindingAware
+   public void accept(Object arg) {
+     ExtractorSpec _t = (ExtractorSpec) arg;
+     _t.service(String.valueOf(getAt(binding.parsed(), "service")));
+   }
+   ```
+
+2. **`LalExpr_0`** — Main class implementing `LalExpression`:
+   ```java
+   public Consumer _consumer0;  // wired after toClass()
+
+   public void execute(Object arg0, Object arg1) {
+     FilterSpec filterSpec = (FilterSpec) arg0;
+     Binding binding = (Binding) arg1;
+     filterSpec.json();
+     ((BindingAware) this._consumer0).setBinding(binding);
+     filterSpec.extractor(this._consumer0);
+     filterSpec.sink();
+   }
+   ```
+
+**Consumer allocation rules**:
+- `json {}` with no `abortOnFailure` → no consumer, emits `filterSpec.json()`
+- `json { abortOnFailure }` → allocates a consumer
+- `text { regexp '...' }` → allocates a consumer
+- `text {}` with no regexp → no consumer, emits `filterSpec.text()`
+- `extractor { ... }` → always allocates a consumer
+- `sink {}` empty → no consumer, emits `filterSpec.sink()`
+- `sink { enforcer {} }` → allocates a consumer
+
+## Dependencies
+
+All within this module (grammar, compiler, and runtime are merged):
+- ANTLR4 grammar → generates lexer/parser at build time
+- `LalExpression`, `Binding`, `FilterSpec`, all Spec classes — in `dsl` package of this module
+- `javassist` — bytecode generation
diff --git a/oap-server/analyzer/log-analyzer/pom.xml b/oap-server/analyzer/log-analyzer/pom.xml
index 576539cb85..ff811b1596 100644
--- a/oap-server/analyzer/log-analyzer/pom.xml
+++ b/oap-server/analyzer/log-analyzer/pom.xml
@@ -7,13 +7,14 @@
   ~ (the "License"); you may not use this file except in compliance with
   ~ the License.  You may obtain a copy of the License at
   ~
-  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~     http://www.apache.org/licenses/LICENSE-2.0
   ~
   ~ Unless required by applicable law or agreed to in writing, software
   ~ distributed under the License is distributed on an "AS IS" BASIS,
   ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   ~ See the License for the specific language governing permissions and
   ~ limitations under the License.
+  ~
   -->
 
 <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
@@ -25,7 +26,6 @@
     <modelVersion>4.0.0</modelVersion>
 
     <artifactId>log-analyzer</artifactId>
-    <packaging>jar</packaging>
 
     <dependencies>
         <dependency>
@@ -43,13 +43,34 @@
             <artifactId>agent-analyzer</artifactId>
             <version>${project.version}</version>
         </dependency>
-        <dependency>
-            <groupId>org.apache.groovy</groupId>
-            <artifactId>groovy</artifactId>
-        </dependency>
         <dependency>
             <groupId>com.fasterxml.jackson.core</groupId>
             <artifactId>jackson-databind</artifactId>
         </dependency>
+        <dependency>
+            <groupId>org.antlr</groupId>
+            <artifactId>antlr4-runtime</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.javassist</groupId>
+            <artifactId>javassist</artifactId>
+        </dependency>
     </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.antlr</groupId>
+                <artifactId>antlr4-maven-plugin</artifactId>
+                <executions>
+                    <execution>
+                        <id>antlr</id>
+                        <goals>
+                            <goal>antlr4</goal>
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
+        </plugins>
+    </build>
 </project>
diff --git a/oap-server/analyzer/log-analyzer/src/main/antlr4/org/apache/skywalking/lal/rt/grammar/LALLexer.g4 b/oap-server/analyzer/log-analyzer/src/main/antlr4/org/apache/skywalking/lal/rt/grammar/LALLexer.g4
new file mode 100644
index 0000000000..4df6f7113a
--- /dev/null
+++ b/oap-server/analyzer/log-analyzer/src/main/antlr4/org/apache/skywalking/lal/rt/grammar/LALLexer.g4
@@ -0,0 +1,175 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+// Log Analysis Language lexer
+lexer grammar LALLexer;
+
+@Header {package org.apache.skywalking.lal.rt.grammar;}
+
+// Keywords - block structure
+FILTER:         'filter';
+TEXT:           'text';
+JSON:          'json';
+YAML:          'yaml';
+EXTRACTOR:     'extractor';
+SINK:          'sink';
+ABORT:         'abort';
+
+// Keywords - extractor statements
+SERVICE:       'service';
+INSTANCE:      'instance';
+ENDPOINT:      'endpoint';
+LAYER:         'layer';
+TRACE_ID:      'traceId';
+SEGMENT_ID:    'segmentId';
+SPAN_ID:       'spanId';
+TIMESTAMP:     'timestamp';
+TAG:           'tag';
+METRICS:       'metrics';
+SLOW_SQL:      'slowSql';
+SAMPLED_TRACE: 'sampledTrace';
+REGEXP:        'regexp';
+ABORT_ON_FAILURE: 'abortOnFailure';
+NAME:          'name';
+VALUE:         'value';
+LABELS:        'labels';
+ID:            'id';
+STATEMENT:     'statement';
+LATENCY:       'latency';
+URI:           'uri';
+REASON:        'reason';
+PROCESS_ID:    'processId';
+DEST_PROCESS_ID: 'destProcessId';
+DETECT_POINT:  'detectPoint';
+COMPONENT_ID:  'componentId';
+REPORT_SERVICE: 'reportService';
+
+// Keywords - sink statements
+SAMPLER:       'sampler';
+RATE_LIMIT:    'rateLimit';
+RPM:           'rpm';
+ENFORCER:      'enforcer';
+DROPPER:       'dropper';
+
+// Keywords - control flow
+IF:            'if';
+ELSE:          'else';
+
+// Keywords - type cast
+AS:            'as';
+STRING_TYPE:   'String';
+LONG_TYPE:     'Long';
+INTEGER_TYPE:  'Integer';
+BOOLEAN_TYPE:  'Boolean';
+
+// Keywords - built-in references
+LOG:           'log';
+PARSED:        'parsed';
+
+// Keywords - utility class references
+PROCESS_REGISTRY: 'ProcessRegistry';
+
+// Comparison and logical operators
+DEQ:           '==';
+NEQ:           '!=';
+AND:           '&&';
+OR:            '||';
+NOT:           '!';
+GT:            '>';
+LT:            '<';
+GTE:           '>=';
+LTE:           '<=';
+
+// Delimiters
+DOT:           '.';
+COMMA:         ',';
+COLON:         ':';
+SEMI:          ';';
+L_PAREN:       '(';
+R_PAREN:       ')';
+L_BRACE:       '{';
+R_BRACE:       '}';
+L_BRACKET:     '[';
+R_BRACKET:     ']';
+QUESTION:      '?';
+ASSIGN:        '=';
+
+// Arithmetic
+PLUS:          '+';
+MINUS:         '-';
+STAR:          '*';
+SLASH:         '/';
+
+// Literals
+TRUE:          'true';
+FALSE:         'false';
+NULL:          'null';
+
+NUMBER
+    : Digit+ ('.' Digit+)?
+    ;
+
+// String literal: single or double quoted
+STRING
+    : '\'' (~['\\\r\n] | EscapeSequence)* '\''
+    | '"' (~["\\\r\n] | EscapeSequence)* '"'
+    ;
+
+// Groovy-style slashy string for regex patterns: $/pattern/$
+SLASHY_STRING
+    : '$/' .*? '/$'
+    ;
+
+// Comments
+LINE_COMMENT
+    : '//' ~[\r\n]* -> channel(HIDDEN)
+    ;
+
+BLOCK_COMMENT
+    : '/*' .*? '*/' -> channel(HIDDEN)
+    ;
+
+// Whitespace
+WS
+    : [ \t\r\n]+ -> channel(HIDDEN)
+    ;
+
+// Identifiers
+IDENTIFIER
+    : Letter LetterOrDigit*
+    ;
+
+// Fragments
+fragment EscapeSequence
+    : '\\' [btnfr"'\\]
+    | '\\' ([0-3]? [0-7])? [0-7]
+    | '\\' .                                 // catch-all for regex escapes like \d, \w, \s
+    ;
+
+fragment Digit
+    : [0-9]
+    ;
+
+fragment Letter
+    : [a-zA-Z_]
+    ;
+
+fragment LetterOrDigit
+    : Letter
+    | [0-9]
+    ;
diff --git a/oap-server/analyzer/log-analyzer/src/main/antlr4/org/apache/skywalking/lal/rt/grammar/LALParser.g4 b/oap-server/analyzer/log-analyzer/src/main/antlr4/org/apache/skywalking/lal/rt/grammar/LALParser.g4
new file mode 100644
index 0000000000..b381aa1573
--- /dev/null
+++ b/oap-server/analyzer/log-analyzer/src/main/antlr4/org/apache/skywalking/lal/rt/grammar/LALParser.g4
@@ -0,0 +1,464 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+// Log Analysis Language parser
+//
+// Covers LAL DSL patterns:
+//   filter { parser {} extractor {} sink {} }
+//   if (tag("LOG_KIND") == "NGINX_ACCESS_LOG") { ... }
+//   text { regexp $/pattern/$ }
+//   json { abortOnFailure true }
+//   extractor { service parsed.service as String; tag 'key': value }
+//   metrics { name "metric_name"; value 1; labels key: val }
+//   slowSql { id parsed.id as String; statement parsed.statement as String; latency parsed.query_time as Long }
+//   sampledTrace { latency parsed.latency as Long; uri parsed.uri as String; ... }
+//   sink { sampler { rateLimit("id") { rpm 6000 } } }
+parser grammar LALParser;
+
+@Header {package org.apache.skywalking.lal.rt.grammar;}
+
+options { tokenVocab=LALLexer; }
+
+// ==================== Top-level ====================
+
+root
+    : filterBlock EOF
+    ;
+
+// ==================== Filter block ====================
+
+filterBlock
+    : FILTER L_BRACE filterContent R_BRACE
+    ;
+
+filterContent
+    : filterStatement*
+    ;
+
+filterStatement
+    : parserBlock
+    | extractorBlock
+    | sinkBlock
+    | ifStatement
+    | abortBlock
+    ;
+
+// ==================== Parser blocks ====================
+
+parserBlock
+    : textBlock
+    | jsonBlock
+    | yamlBlock
+    ;
+
+textBlock
+    : TEXT L_BRACE textContent R_BRACE
+    ;
+
+textContent
+    : (regexpStatement | abortOnFailureStatement)*
+    ;
+
+regexpStatement
+    : REGEXP regexpPattern
+    ;
+
+regexpPattern
+    : SLASHY_STRING
+    | STRING
+    ;
+
+jsonBlock
+    : JSON L_BRACE jsonContent R_BRACE
+    ;
+
+jsonContent
+    : abortOnFailureStatement?
+    ;
+
+yamlBlock
+    : YAML L_BRACE yamlContent R_BRACE
+    ;
+
+yamlContent
+    : abortOnFailureStatement?
+    ;
+
+abortOnFailureStatement
+    : ABORT_ON_FAILURE boolValue
+    ;
+
+abortBlock
+    : ABORT L_BRACE R_BRACE
+    ;
+
+// ==================== Extractor block ====================
+
+extractorBlock
+    : EXTRACTOR L_BRACE extractorContent R_BRACE
+    ;
+
+extractorContent
+    : extractorStatement*
+    ;
+
+extractorStatement
+    : serviceStatement
+    | instanceStatement
+    | endpointStatement
+    | layerStatement
+    | traceIdStatement
+    | segmentIdStatement
+    | spanIdStatement
+    | timestampStatement
+    | tagStatement
+    | metricsBlock
+    | slowSqlBlock
+    | sampledTraceBlock
+    | ifStatement
+    ;
+
+serviceStatement
+    : SERVICE valueAccess typeCast?
+    ;
+
+instanceStatement
+    : INSTANCE valueAccess typeCast?
+    ;
+
+endpointStatement
+    : ENDPOINT valueAccess typeCast?
+    ;
+
+layerStatement
+    : LAYER valueAccess typeCast?
+    ;
+
+traceIdStatement
+    : TRACE_ID valueAccess typeCast?
+    ;
+
+segmentIdStatement
+    : SEGMENT_ID valueAccess typeCast?
+    ;
+
+spanIdStatement
+    : SPAN_ID valueAccess typeCast?
+    ;
+
+timestampStatement
+    : TIMESTAMP valueAccess typeCast? (COMMA STRING)?
+    ;
+
+tagStatement
+    : TAG tagMap
+    | TAG STRING COLON valueAccess typeCast?
+    ;
+
+tagMap
+    : anyIdentifier COLON valueAccess typeCast? (COMMA anyIdentifier COLON valueAccess typeCast?)*
+    ;
+
+// ==================== Metrics block ====================
+
+metricsBlock
+    : METRICS L_BRACE metricsContent R_BRACE
+    ;
+
+metricsContent
+    : metricsStatement*
+    ;
+
+metricsStatement
+    : metricsNameStatement
+    | metricsTimestampStatement
+    | metricsLabelsStatement
+    | metricsValueStatement
+    ;
+
+metricsNameStatement
+    : NAME valueAccess typeCast?
+    ;
+
+metricsTimestampStatement
+    : TIMESTAMP valueAccess typeCast?
+    ;
+
+metricsLabelsStatement
+    : LABELS labelMap
+    ;
+
+labelMap
+    : labelEntry (COMMA labelEntry)*
+    ;
+
+labelEntry
+    : anyIdentifier COLON valueAccess typeCast?
+    ;
+
+metricsValueStatement
+    : VALUE valueAccess typeCast?
+    ;
+
+// ==================== Slow SQL block ====================
+
+slowSqlBlock
+    : SLOW_SQL L_BRACE slowSqlContent R_BRACE
+    ;
+
+slowSqlContent
+    : slowSqlStatement*
+    ;
+
+slowSqlStatement
+    : slowSqlIdStatement
+    | slowSqlStatementStatement
+    | slowSqlLatencyStatement
+    ;
+
+slowSqlIdStatement
+    : ID valueAccess typeCast?
+    ;
+
+slowSqlStatementStatement
+    : STATEMENT valueAccess typeCast?
+    ;
+
+slowSqlLatencyStatement
+    : LATENCY valueAccess typeCast?
+    ;
+
+// ==================== Sampled trace block ====================
+
+sampledTraceBlock
+    : SAMPLED_TRACE L_BRACE sampledTraceContent R_BRACE
+    ;
+
+sampledTraceContent
+    : sampledTraceStatement*
+    ;
+
+sampledTraceStatement
+    : sampledTraceLatencyStatement
+    | sampledTraceUriStatement
+    | sampledTraceReasonStatement
+    | sampledTraceProcessIdStatement
+    | sampledTraceDestProcessIdStatement
+    | sampledTraceDetectPointStatement
+    | sampledTraceComponentIdStatement
+    | reportServiceStatement
+    | ifStatement
+    ;
+
+sampledTraceLatencyStatement
+    : LATENCY valueAccess typeCast?
+    ;
+
+sampledTraceUriStatement
+    : URI valueAccess typeCast?
+    ;
+
+sampledTraceReasonStatement
+    : REASON valueAccess typeCast?
+    ;
+
+sampledTraceProcessIdStatement
+    : PROCESS_ID valueAccess typeCast?
+    ;
+
+sampledTraceDestProcessIdStatement
+    : DEST_PROCESS_ID valueAccess typeCast?
+    ;
+
+sampledTraceDetectPointStatement
+    : DETECT_POINT valueAccess typeCast?
+    ;
+
+sampledTraceComponentIdStatement
+    : COMPONENT_ID valueAccess typeCast?
+    ;
+
+reportServiceStatement
+    : REPORT_SERVICE valueAccess typeCast?
+    ;
+
+// ==================== Sink block ====================
+
+sinkBlock
+    : SINK L_BRACE sinkContent R_BRACE
+    ;
+
+sinkContent
+    : sinkStatement*
+    ;
+
+sinkStatement
+    : samplerBlock
+    | enforcerStatement
+    | dropperStatement
+    | ifStatement
+    ;
+
+samplerBlock
+    : SAMPLER L_BRACE samplerContent R_BRACE
+    ;
+
+samplerContent
+    : (rateLimitBlock | ifStatement)*
+    ;
+
+rateLimitBlock
+    : RATE_LIMIT L_PAREN rateLimitId R_PAREN L_BRACE rateLimitContent R_BRACE
+    ;
+
+rateLimitId
+    : STRING
+    ;
+
+rateLimitContent
+    : RPM NUMBER
+    ;
+
+enforcerStatement
+    : ENFORCER L_BRACE R_BRACE
+    ;
+
+dropperStatement
+    : DROPPER L_BRACE R_BRACE
+    ;
+
+// ==================== Control flow ====================
+
+ifStatement
+    : IF L_PAREN condition R_PAREN L_BRACE
+        ifBody
+      R_BRACE
+      (ELSE IF L_PAREN condition R_PAREN L_BRACE
+        ifBody
+      R_BRACE)*
+      (ELSE L_BRACE
+        ifBody
+      R_BRACE)?
+    ;
+
+ifBody
+    : filterStatement*
+    | extractorStatement*
+    | sinkStatement*
+    | sampledTraceStatement*
+    | samplerContent
+    ;
+
+// ==================== Conditions ====================
+
+condition
+    : condition AND condition                     # condAnd
+    | condition OR condition                      # condOr
+    | NOT condition                               # condNot
+    | L_PAREN condition R_PAREN                   # condParen
+    | conditionExpr DEQ conditionExpr             # condEq
+    | conditionExpr NEQ conditionExpr             # condNeq
+    | conditionExpr GT conditionExpr              # condGt
+    | conditionExpr LT conditionExpr              # condLt
+    | conditionExpr GTE conditionExpr             # condGte
+    | conditionExpr LTE conditionExpr             # condLte
+    | conditionExpr                               # condSingle
+    ;
+
+conditionExpr
+    : valueAccess typeCast?                       # condValueAccess
+    | STRING                                      # condString
+    | NUMBER                                      # condNumber
+    | boolValue                                   # condBool
+    | NULL                                        # condNull
+    | functionInvocation                          # condFunctionCall
+    ;
+
+// ==================== Value access ====================
+
+// Accessing parsed values, log fields, and method calls:
+//   parsed.level, parsed?.response?.responseCode?.value
+//   log.service, log.timestamp, log.serviceInstance
+//   tag("LOG_KIND")
+//   ProcessRegistry.generateVirtualLocalProcess(...)
+
+valueAccess
+    : valueAccessPrimary (valueAccessSegment)*
+    ;
+
+valueAccessPrimary
+    : PARSED                                      # valueParsed
+    | LOG                                         # valueLog
+    | IDENTIFIER                                  # valueIdentifier
+    | STRING                                      # valueString
+    | NUMBER                                      # valueNumber
+    | boolValue                                   # valueBool
+    | NULL                                        # valueNull
+    | functionInvocation                          # valueFunctionCall
+    ;
+
+valueAccessSegment
+    : DOT anyIdentifier                           # segmentField
+    | QUESTION DOT anyIdentifier                  # segmentSafeField
+    | DOT functionInvocation                      # segmentMethod
+    | QUESTION DOT functionInvocation             # segmentSafeMethod
+    ;
+
+functionInvocation
+    : IDENTIFIER L_PAREN functionArgList? R_PAREN
+    ;
+
+functionArgList
+    : functionArg (COMMA functionArg)*
+    ;
+
+functionArg
+    : valueAccess typeCast?
+    | STRING
+    | NUMBER
+    | boolValue
+    | NULL
+    ;
+
+// ==================== Type cast ====================
+
+typeCast
+    : AS (STRING_TYPE | LONG_TYPE | INTEGER_TYPE | BOOLEAN_TYPE)
+    ;
+
+// ==================== Common ====================
+
+// Allows keywords to be used as identifiers in contexts like field names,
+// labels, and value access segments (e.g. parsed.service, parsed.layer).
+anyIdentifier
+    : IDENTIFIER
+    | SERVICE | INSTANCE | ENDPOINT | LAYER
+    | TRACE_ID | SEGMENT_ID | SPAN_ID | TIMESTAMP
+    | TAG | METRICS | SLOW_SQL | SAMPLED_TRACE
+    | REGEXP | ABORT_ON_FAILURE
+    | NAME | VALUE | LABELS
+    | ID | STATEMENT | LATENCY
+    | URI | REASON | PROCESS_ID | DEST_PROCESS_ID
+    | DETECT_POINT | COMPONENT_ID | REPORT_SERVICE
+    | SAMPLER | RATE_LIMIT | RPM | ENFORCER | DROPPER
+    | TEXT | JSON | YAML | FILTER | EXTRACTOR | SINK | ABORT
+    ;
+
+boolValue
+    : TRUE | FALSE
+    ;
diff --git a/oap-server/analyzer/log-analyzer/src/main/java/org/apache/skywalking/oap/log/analyzer/compiler/LALClassGenerator.java b/oap-server/analyzer/log-analyzer/src/main/java/org/apache/skywalking/oap/log/analyzer/compiler/LALClassGenerator.java
new file mode 100644
index 0000000000..092d9f45f1
--- /dev/null
+++ b/oap-server/analyzer/log-analyzer/src/main/java/org/apache/skywalking/oap/log/analyzer/compiler/LALClassGenerator.java
@@ -0,0 +1,627 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.skywalking.oap.log.analyzer.compiler;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.atomic.AtomicInteger;
+import javassist.ClassPool;
+import javassist.CtClass;
+import javassist.CtField;
+import javassist.CtNewConstructor;
+import javassist.CtNewMethod;
+import org.apache.skywalking.oap.log.analyzer.compiler.rt.LalExpressionPackageHolder;
+import org.apache.skywalking.oap.log.analyzer.dsl.LalExpression;
+
+/**
+ * Generates {@link LalExpression} implementation classes from
+ * {@link LALScriptModel} AST using Javassist bytecode generation.
+ *
+ * <p>Because Javassist cannot compile anonymous inner classes,
+ * Consumer callbacks are pre-compiled as separate classes and
+ * stored as fields on the main class.
+ */
+public final class LALClassGenerator {
+
+    private static final AtomicInteger CLASS_COUNTER = new AtomicInteger(0);
+
+    private static final String PACKAGE_PREFIX =
+        "org.apache.skywalking.oap.log.analyzer.compiler.rt.";
+
+    private static final String FILTER_SPEC =
+        "org.apache.skywalking.oap.log.analyzer.dsl.spec.filter.FilterSpec";
+    private static final String BINDING =
+        "org.apache.skywalking.oap.log.analyzer.dsl.Binding";
+    private static final String BINDING_PARSED =
+        "org.apache.skywalking.oap.log.analyzer.dsl.Binding.Parsed";
+
+    private final ClassPool classPool;
+
+    public LALClassGenerator() {
+        this(ClassPool.getDefault());
+    }
+
+    public LALClassGenerator(final ClassPool classPool) {
+        this.classPool = classPool;
+    }
+
+    /**
+     * Compiles a LAL DSL script into a LalExpression implementation.
+     */
+    public LalExpression compile(final String dsl) throws Exception {
+        final LALScriptModel model = LALScriptParser.parse(dsl);
+        return compileFromModel(model);
+    }
+
+    /**
+     * Compiles from a pre-parsed model.
+     */
+    public LalExpression compileFromModel(final LALScriptModel model) throws Exception {
+        final String className = PACKAGE_PREFIX + "LalExpr_"
+            + CLASS_COUNTER.getAndIncrement();
+
+        // Phase 1: Collect all consumer info in traversal order
+        final List<ConsumerInfo> consumers = new ArrayList<>();
+        collectConsumers(model.getStatements(), consumers);
+
+        // Phase 2: Compile consumer classes
+        final List<Object> consumerInstances = new ArrayList<>();
+        for (int i = 0; i < consumers.size(); i++) {
+            final String consumerName = className + "_C" + i;
+            final Object instance = compileConsumerClass(
+                consumerName, consumers.get(i));
+            consumerInstances.add(instance);
+        }
+
+        // Phase 3: Build main class with consumer fields
+        final CtClass ctClass = classPool.makeClass(className);
+        ctClass.addInterface(classPool.get(
+            "org.apache.skywalking.oap.log.analyzer.dsl.LalExpression"));
+
+        for (int i = 0; i < consumers.size(); i++) {
+            ctClass.addField(CtField.make(
+                "public java.util.function.Consumer _consumer" + i + ";",
+                ctClass));
+        }
+
+        ctClass.addConstructor(CtNewConstructor.defaultConstructor(ctClass));
+        addHelperMethods(ctClass);
+
+        // Phase 4: Generate execute method referencing consumer fields
+        final int[] counter = {0};
+        final String executeBody = generateExecuteMethod(model, counter);
+        ctClass.addMethod(CtNewMethod.make(executeBody, ctClass));
+
+        final Class<?> clazz = ctClass.toClass(LalExpressionPackageHolder.class);
+        ctClass.detach();
+        final LalExpression instance = (LalExpression) clazz
+            .getDeclaredConstructor().newInstance();
+
+        // Phase 5: Wire consumer fields
+        for (int i = 0; i < consumerInstances.size(); i++) {
+            clazz.getField("_consumer" + i).set(instance, consumerInstances.get(i));
+        }
+
+        return instance;
+    }
+
+    // ==================== Consumer info ====================
+
+    private static final class ConsumerInfo {
+        final String body;
+        final String castType;
+        final List<ConsumerInfo> subConsumers;
+
+        ConsumerInfo(final String body, final String castType) {
+            this.body = body;
+            this.castType = castType;
+            this.subConsumers = new ArrayList<>();
+        }
+    }
+
+    // ==================== Phase 1: Collect consumers ====================
+
+    private void collectConsumers(
+            final List<? extends LALScriptModel.FilterStatement> stmts,
+            final List<ConsumerInfo> consumers) {
+        for (final LALScriptModel.FilterStatement stmt : stmts) {
+            collectConsumerFromStatement(stmt, consumers);
+        }
+    }
+
+    private void collectConsumerFromStatement(
+            final LALScriptModel.FilterStatement stmt,
+            final List<ConsumerInfo> consumers) {
+        if (stmt instanceof LALScriptModel.TextParser) {
+            final LALScriptModel.TextParser tp = (LALScriptModel.TextParser) stmt;
+            if (tp.getRegexpPattern() != null) {
+                final StringBuilder sb = new StringBuilder();
+                sb.append("  _t.regexp(\"")
+                  .append(escapeJava(tp.getRegexpPattern()))
+                  .append("\");\n");
+                consumers.add(new ConsumerInfo(sb.toString(),
+                    "org.apache.skywalking.oap.log.analyzer.dsl"
+                    + ".spec.parser.TextParserSpec"));
+            }
+        } else if (stmt instanceof LALScriptModel.JsonParser) {
+            if (((LALScriptModel.JsonParser) stmt).isAbortOnFailure()) {
+                consumers.add(new ConsumerInfo(
+                    "  _t.abortOnFailure();\n",
+                    "org.apache.skywalking.oap.log.analyzer.dsl"
+                    + ".spec.parser.JsonParserSpec"));
+            }
+        } else if (stmt instanceof LALScriptModel.YamlParser) {
+            if (((LALScriptModel.YamlParser) stmt).isAbortOnFailure()) {
+                consumers.add(new ConsumerInfo(
+                    "  _t.abortOnFailure();\n",
+                    "org.apache.skywalking.oap.log.analyzer.dsl"
+                    + ".spec.parser.YamlParserSpec"));
+            }
+        } else if (stmt instanceof LALScriptModel.ExtractorBlock) {
+            final LALScriptModel.ExtractorBlock block =
+                (LALScriptModel.ExtractorBlock) stmt;
+            final StringBuilder sb = new StringBuilder();
+            generateExtractorStatementsFlat(sb, block.getStatements());
+            consumers.add(new ConsumerInfo(sb.toString(),
+                "org.apache.skywalking.oap.log.analyzer.dsl"
+                + ".spec.extractor.ExtractorSpec"));
+        } else if (stmt instanceof LALScriptModel.SinkBlock) {
+            final LALScriptModel.SinkBlock sink = (LALScriptModel.SinkBlock) stmt;
+            if (!sink.getStatements().isEmpty()) {
+                final StringBuilder sb = new StringBuilder();
+                generateSinkStatementsFlat(sb, sink.getStatements());
+                consumers.add(new ConsumerInfo(sb.toString(),
+                    "org.apache.skywalking.oap.log.analyzer.dsl"
+                    + ".spec.sink.SinkSpec"));
+            }
+        } else if (stmt instanceof LALScriptModel.IfBlock) {
+            final LALScriptModel.IfBlock ifBlock = (LALScriptModel.IfBlock) stmt;
+            collectConsumers(ifBlock.getThenBranch(), consumers);
+            if (!ifBlock.getElseBranch().isEmpty()) {
+                collectConsumers(ifBlock.getElseBranch(), consumers);
+            }
+        }
+    }
+
+    // ==================== Flat code for consumer bodies ====================
+
+    private void generateExtractorStatementsFlat(
+            final StringBuilder sb,
+            final List<LALScriptModel.ExtractorStatement> stmts) {
+        for (final LALScriptModel.ExtractorStatement stmt : stmts) {
+            if (stmt instanceof LALScriptModel.FieldAssignment) {
+                final LALScriptModel.FieldAssignment field =
+                    (LALScriptModel.FieldAssignment) stmt;
+                sb.append("  _t.").append(field.getFieldType().name().toLowerCase())
+                  .append("(");
+                generateCastedValueAccess(sb, field.getValue(),
+                    field.getCastType());
+                if (field.getFormatPattern() != null) {
+                    sb.append(", \"")
+                      .append(escapeJava(field.getFormatPattern()))
+                      .append("\"");
+                }
+                sb.append(");\n");
+            } else if (stmt instanceof LALScriptModel.TagAssignment) {
+                final LALScriptModel.TagAssignment tag =
+                    (LALScriptModel.TagAssignment) stmt;
+                if (tag.getTags().size() == 1) {
+                    final Map.Entry<String, LALScriptModel.TagValue> entry =
+                        tag.getTags().entrySet().iterator().next();
+                    sb.append("  _t.tag(\"")
+                      .append(escapeJava(entry.getKey())).append("\", ");
+                    generateCastedValueAccess(sb, entry.getValue().getValue(),
+                        entry.getValue().getCastType());
+                    sb.append(");\n");
+                }
+            }
+        }
+    }
+
+    private void generateSinkStatementsFlat(
+            final StringBuilder sb,
+            final List<LALScriptModel.SinkStatement> stmts) {
+        for (final LALScriptModel.SinkStatement stmt : stmts) {
+            if (stmt instanceof LALScriptModel.EnforcerStatement) {
+                sb.append("  _t.enforcer();\n");
+            } else if (stmt instanceof LALScriptModel.DropperStatement) {
+                sb.append("  _t.dropper();\n");
+            }
+        }
+    }
+
+    // ==================== Phase 2: Compile consumer classes ====================
+
+    private Object compileConsumerClass(final String className,
+                                         final ConsumerInfo info) throws Exception {
+        final CtClass ctClass = classPool.makeClass(className);
+        ctClass.addInterface(classPool.get("java.util.function.Consumer"));
+        ctClass.addInterface(classPool.get(
+            PACKAGE_PREFIX + "BindingAware"));
+        ctClass.addConstructor(CtNewConstructor.defaultConstructor(ctClass));
+
+        ctClass.addField(CtField.make(
+            "private " + BINDING + " binding;", ctClass));
+
+        ctClass.addMethod(CtNewMethod.make(
+            "public void setBinding(" + BINDING + " b) {"
+            + " this.binding = b; }", ctClass));
+        ctClass.addMethod(CtNewMethod.make(
+            "public " + BINDING + " getBinding() {"
+            + " return this.binding; }", ctClass));
+
+        addHelperMethods(ctClass);
+
+        final String method = "public void accept(Object arg) {\n"
+            + "  " + info.castType + " _t = (" + info.castType + ") arg;\n"
+            + info.body
+            + "}\n";
+        ctClass.addMethod(CtNewMethod.make(method, ctClass));
+
+        final Class<?> clazz = ctClass.toClass(LalExpressionPackageHolder.class);
+        ctClass.detach();
+        return clazz.getDeclaredConstructor().newInstance();
+    }
+
+    // ==================== Phase 4: Generate execute method ====================
+
+    private String generateExecuteMethod(final LALScriptModel model,
+                                          final int[] counter) {
+        final StringBuilder sb = new StringBuilder();
+        sb.append("public void execute(Object arg0, Object arg1) {\n");
+        sb.append("  ").append(FILTER_SPEC).append(" filterSpec = (")
+          .append(FILTER_SPEC).append(") arg0;\n");
+        sb.append("  ").append(BINDING).append(" binding = (")
+          .append(BINDING).append(") arg1;\n");
+
+        for (final LALScriptModel.FilterStatement stmt
+                : model.getStatements()) {
+            generateFilterStatement(sb, stmt, counter);
+        }
+
+        sb.append("}\n");
+        return sb.toString();
+    }
+
+    private void generateFilterStatement(final StringBuilder sb,
+                                          final LALScriptModel.FilterStatement stmt,
+                                          final int[] counter) {
+        if (stmt instanceof LALScriptModel.TextParser) {
+            final LALScriptModel.TextParser tp = (LALScriptModel.TextParser) stmt;
+            if (tp.getRegexpPattern() != null) {
+                emitConsumerCall(sb, "filterSpec.text", counter);
+            } else {
+                sb.append("  filterSpec.text();\n");
+            }
+        } else if (stmt instanceof LALScriptModel.JsonParser) {
+            if (((LALScriptModel.JsonParser) stmt).isAbortOnFailure()) {
+                emitConsumerCall(sb, "filterSpec.json", counter);
+            } else {
+                sb.append("  filterSpec.json();\n");
+            }
+        } else if (stmt instanceof LALScriptModel.YamlParser) {
+            if (((LALScriptModel.YamlParser) stmt).isAbortOnFailure()) {
+                emitConsumerCall(sb, "filterSpec.yaml", counter);
+            } else {
+                sb.append("  filterSpec.yaml();\n");
+            }
+        } else if (stmt instanceof LALScriptModel.AbortStatement) {
+            sb.append("  filterSpec.abort();\n");
+        } else if (stmt instanceof LALScriptModel.ExtractorBlock) {
+            emitConsumerCall(sb, "filterSpec.extractor", counter);
+        } else if (stmt instanceof LALScriptModel.SinkBlock) {
+            final LALScriptModel.SinkBlock sink = (LALScriptModel.SinkBlock) stmt;
+            if (sink.getStatements().isEmpty()) {
+                sb.append("  filterSpec.sink();\n");
+            } else {
+                emitConsumerCall(sb, "filterSpec.sink", counter);
+            }
+        } else if (stmt instanceof LALScriptModel.IfBlock) {
+            generateIfBlock(sb, (LALScriptModel.IfBlock) stmt, counter);
+        }
+    }
+
+    private void emitConsumerCall(final StringBuilder sb,
+                                   final String methodPrefix,
+                                   final int[] counter) {
+        final int idx = counter[0]++;
+        sb.append("  ((")
+          .append(PACKAGE_PREFIX).append("BindingAware) this._consumer")
+          .append(idx).append(").setBinding(binding);\n");
+        sb.append("  ").append(methodPrefix)
+          .append("(this._consumer").append(idx).append(");\n");
+    }
+
+    private void generateIfBlock(final StringBuilder sb,
+                                  final LALScriptModel.IfBlock ifBlock,
+                                  final int[] counter) {
+        sb.append("  if (");
+        generateCondition(sb, ifBlock.getCondition());
+        sb.append(") {\n");
+        for (final LALScriptModel.FilterStatement s : ifBlock.getThenBranch()) {
+            generateFilterStatement(sb, s, counter);
+        }
+        sb.append("  }\n");
+        if (!ifBlock.getElseBranch().isEmpty()) {
+            sb.append("  else {\n");
+            for (final LALScriptModel.FilterStatement s
+                    : ifBlock.getElseBranch()) {
+                generateFilterStatement(sb, s, counter);
+            }
+            sb.append("  }\n");
+        }
+    }
+
+    // ==================== Helper methods ====================
+
+    private void addHelperMethods(final CtClass ctClass) throws Exception {
+        ctClass.addMethod(CtNewMethod.make(
+            "private static Object getAt(Object obj, String key) {"
+            + "  if (obj == null) return null;"
+            + "  if (obj instanceof " + BINDING_PARSED + ")"
+            + "    return ((" + BINDING_PARSED + ") obj).getAt(key);"
+            + "  if (obj instanceof java.util.Map)"
+            + "    return ((java.util.Map) obj).get(key);"
+            + "  return null;"
+            + "}", ctClass));
+
+        ctClass.addMethod(CtNewMethod.make(
+            "private static long toLong(Object obj) {"
+            + "  if (obj instanceof Number) return ((Number) obj).longValue();"
+            + "  if (obj instanceof String) return Long.parseLong((String) obj);"
+            + "  return 0L;"
+            + "}", ctClass));
+
+        ctClass.addMethod(CtNewMethod.make(
+            "private static int toInt(Object obj) {"
+            + "  if (obj instanceof Number) return ((Number) obj).intValue();"
+            + "  if (obj instanceof String) return Integer.parseInt((String) obj);"
+            + "  return 0;"
+            + "}", ctClass));
+
+        ctClass.addMethod(CtNewMethod.make(
+            "private static boolean toBool(Object obj) {"
+            + "  if (obj instanceof Boolean) return ((Boolean) obj).booleanValue();"
+            + "  if (obj instanceof String)"
+            + " return Boolean.parseBoolean((String) obj);"
+            + "  return obj != null;"
+            + "}", ctClass));
+
+        ctClass.addMethod(CtNewMethod.make(
+            "private static boolean isTruthy(Object obj) {"
+            + "  if (obj == null) return false;"
+            + "  if (obj instanceof Boolean)"
+            + " return ((Boolean) obj).booleanValue();"
+            + "  if (obj instanceof String)"
+            + " return !((String) obj).isEmpty();"
+            + "  if (obj instanceof Number)"
+            + " return ((Number) obj).doubleValue() != 0;"
+            + "  return true;"
+            + "}", ctClass));
+    }
+
+    // ==================== Conditions ====================
+
+    private void generateCondition(final StringBuilder sb,
+                                    final LALScriptModel.Condition cond) {
+        if (cond instanceof LALScriptModel.ComparisonCondition) {
+            final LALScriptModel.ComparisonCondition cc =
+                (LALScriptModel.ComparisonCondition) cond;
+            switch (cc.getOp()) {
+                case EQ:
+                    sb.append("java.util.Objects.equals(");
+                    generateValueAccessObj(sb, cc.getLeft(), cc.getLeftCast());
+                    sb.append(", ");
+                    generateConditionValue(sb, cc.getRight());
+                    sb.append(")");
+                    break;
+                case NEQ:
+                    sb.append("!java.util.Objects.equals(");
+                    generateValueAccessObj(sb, cc.getLeft(), cc.getLeftCast());
+                    sb.append(", ");
+                    generateConditionValue(sb, cc.getRight());
+                    sb.append(")");
+                    break;
+                case GT:
+                    sb.append("toLong(");
+                    generateValueAccessObj(sb, cc.getLeft(), null);
+                    sb.append(") > ");
+                    generateConditionValueNumeric(sb, cc.getRight());
+                    break;
+                case LT:
+                    sb.append("toLong(");
+                    generateValueAccessObj(sb, cc.getLeft(), null);
+                    sb.append(") < ");
+                    generateConditionValueNumeric(sb, cc.getRight());
+                    break;
+                case GTE:
+                    sb.append("toLong(");
+                    generateValueAccessObj(sb, cc.getLeft(), null);
+                    sb.append(") >= ");
+                    generateConditionValueNumeric(sb, cc.getRight());
+                    break;
+                case LTE:
+                    sb.append("toLong(");
+                    generateValueAccessObj(sb, cc.getLeft(), null);
+                    sb.append(") <= ");
+                    generateConditionValueNumeric(sb, cc.getRight());
+                    break;
+                default:
+                    break;
+            }
+        } else if (cond instanceof LALScriptModel.LogicalCondition) {
+            final LALScriptModel.LogicalCondition lc =
+                (LALScriptModel.LogicalCondition) cond;
+            sb.append("(");
+            generateCondition(sb, lc.getLeft());
+            sb.append(lc.getOp() == LALScriptModel.LogicalOp.AND
+                ? " && " : " || ");
+            generateCondition(sb, lc.getRight());
+            sb.append(")");
+        } else if (cond instanceof LALScriptModel.NotCondition) {
+            sb.append("!(");
+            generateCondition(sb,
+                ((LALScriptModel.NotCondition) cond).getInner());
+            sb.append(")");
+        } else if (cond instanceof LALScriptModel.ExprCondition) {
+            sb.append("isTruthy(");
+            generateValueAccessObj(sb,
+                ((LALScriptModel.ExprCondition) cond).getExpr(),
+                ((LALScriptModel.ExprCondition) cond).getCastType());
+            sb.append(")");
+        }
+    }
+
+    private void generateConditionValue(final StringBuilder sb,
+                                         final LALScriptModel.ConditionValue cv) {
+        if (cv instanceof LALScriptModel.StringConditionValue) {
+            sb.append('"')
+              .append(escapeJava(
+                  ((LALScriptModel.StringConditionValue) cv).getValue()))
+              .append('"');
+        } else if (cv instanceof LALScriptModel.NumberConditionValue) {
+            final double val =
+                ((LALScriptModel.NumberConditionValue) cv).getValue();
+            sb.append("Long.valueOf(").append((long) val).append("L)");
+        } else if (cv instanceof LALScriptModel.BoolConditionValue) {
+            sb.append("Boolean.valueOf(")
+              .append(((LALScriptModel.BoolConditionValue) cv).isValue())
+              .append(")");
+        } else if (cv instanceof LALScriptModel.NullConditionValue) {
+            sb.append("null");
+        } else if (cv instanceof LALScriptModel.ValueAccessConditionValue) {
+            generateValueAccessObj(sb,
+                ((LALScriptModel.ValueAccessConditionValue) cv).getValue(),
+                null);
+        }
+    }
+
+    private void generateConditionValueNumeric(
+            final StringBuilder sb,
+            final LALScriptModel.ConditionValue cv) {
+        if (cv instanceof LALScriptModel.NumberConditionValue) {
+            sb.append((long) ((LALScriptModel.NumberConditionValue) cv)
+                .getValue()).append("L");
+        } else if (cv instanceof LALScriptModel.ValueAccessConditionValue) {
+            sb.append("toLong(");
+            generateValueAccessObj(sb,
+                ((LALScriptModel.ValueAccessConditionValue) cv).getValue(),
+                null);
+            sb.append(")");
+        } else {
+            sb.append("0L");
+        }
+    }
+
+    // ==================== Value access ====================
+
+    private void generateCastedValueAccess(final StringBuilder sb,
+                                            final LALScriptModel.ValueAccess value,
+                                            final String castType) {
+        if ("String".equals(castType)) {
+            sb.append("String.valueOf(");
+            generateValueAccess(sb, value);
+            sb.append(")");
+        } else if ("Long".equals(castType)) {
+            sb.append("toLong(");
+            generateValueAccess(sb, value);
+            sb.append(")");
+        } else if ("Integer".equals(castType)) {
+            sb.append("toInt(");
+            generateValueAccess(sb, value);
+            sb.append(")");
+        } else if ("Boolean".equals(castType)) {
+            sb.append("toBool(");
+            generateValueAccess(sb, value);
+            sb.append(")");
+        } else {
+            generateValueAccess(sb, value);
+        }
+    }
+
+    private void generateValueAccessObj(final StringBuilder sb,
+                                         final LALScriptModel.ValueAccess value,
+                                         final String castType) {
+        if ("String".equals(castType)) {
+            sb.append("String.valueOf(");
+            generateValueAccess(sb, value);
+            sb.append(")");
+        } else {
+            generateValueAccess(sb, value);
+        }
+    }
+
+    private void generateValueAccess(final StringBuilder sb,
+                                      final LALScriptModel.ValueAccess value) {
+        String current;
+        if (value.isParsedRef()) {
+            current = "binding.parsed()";
+        } else if (value.isLogRef()) {
+            current = "binding.log()";
+        } else {
+            final List<LALScriptModel.ValueAccessSegment> segs = value.getChain();
+            if (segs.isEmpty()) {
+                sb.append("null");
+                return;
+            }
+            current = "binding.parsed()";
+        }
+
+        final List<LALScriptModel.ValueAccessSegment> chain = value.getChain();
+        if (chain.isEmpty()) {
+            sb.append(current);
+            return;
+        }
+
+        for (int i = 0; i < chain.size(); i++) {
+            final LALScriptModel.ValueAccessSegment seg = chain.get(i);
+            if (seg instanceof LALScriptModel.FieldSegment) {
+                final String name =
+                    ((LALScriptModel.FieldSegment) seg).getName();
+                current = "getAt(" + current + ", \""
+                    + escapeJava(name) + "\")";
+            } else if (seg instanceof LALScriptModel.MethodSegment) {
+                final LALScriptModel.MethodSegment ms =
+                    (LALScriptModel.MethodSegment) seg;
+                current = current + "." + ms.getName() + "()";
+            }
+        }
+        sb.append(current);
+    }
+
+    // ==================== Utilities ====================
+
+    private static String escapeJava(final String s) {
+        return s.replace("\\", "\\\\")
+                .replace("\"", "\\\"")
+                .replace("\n", "\\n")
+                .replace("\r", "\\r")
+                .replace("\t", "\\t");
+    }
+
+    /**
+     * Generates the Java source body of the execute method for
+     * debugging/testing.
+     */
+    public String generateSource(final String dsl) {
+        final LALScriptModel model = LALScriptParser.parse(dsl);
+        final int[] counter = {0};
+        return generateExecuteMethod(model, counter);
+    }
+}
diff --git a/oap-server/analyzer/log-analyzer/src/main/java/org/apache/skywalking/oap/log/analyzer/compiler/LALScriptModel.java b/oap-server/analyzer/log-analyzer/src/main/java/org/apache/skywalking/oap/log/analyzer/compiler/LALScriptModel.java
new file mode 100644
index 0000000000..cc3d1d3df0
--- /dev/null
+++ b/oap-server/analyzer/log-analyzer/src/main/java/org/apache/skywalking/oap/log/analyzer/compiler/LALScriptModel.java
@@ -0,0 +1,459 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.skywalking.oap.log.analyzer.compiler;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import lombok.Getter;
+
+/**
+ * Immutable AST model for LAL (Log Analysis Language) scripts.
+ *
+ * <p>Represents parsed scripts like:
+ * <pre>
+ *   filter {
+ *     json {}
+ *     extractor { service parsed.service as String }
+ *     sink { sampler { rateLimit("id") { rpm 6000 } } }
+ *   }
+ * </pre>
+ */
+public final class LALScriptModel {
+
+    @Getter
+    private final List<FilterStatement> statements;
+
+    public LALScriptModel(final List<FilterStatement> statements) {
+        this.statements = Collections.unmodifiableList(statements);
+    }
+
+    // ==================== Filter statements ====================
+
+    public interface FilterStatement {
+    }
+
+    // ==================== Parser blocks ====================
+
+    @Getter
+    public static final class TextParser implements FilterStatement {
+        private final String regexpPattern;
+        private final boolean abortOnFailure;
+
+        public TextParser(final String regexpPattern, final boolean abortOnFailure) {
+            this.regexpPattern = regexpPattern;
+            this.abortOnFailure = abortOnFailure;
+        }
+    }
+
+    @Getter
+    public static final class JsonParser implements FilterStatement {
+        private final boolean abortOnFailure;
+
+        public JsonParser(final boolean abortOnFailure) {
+            this.abortOnFailure = abortOnFailure;
+        }
+    }
+
+    @Getter
+    public static final class YamlParser implements FilterStatement {
+        private final boolean abortOnFailure;
+
+        public YamlParser(final boolean abortOnFailure) {
+            this.abortOnFailure = abortOnFailure;
+        }
+    }
+
+    public static final class AbortStatement implements FilterStatement {
+    }
+
+    // ==================== Extractor block ====================
+
+    @Getter
+    public static final class ExtractorBlock implements FilterStatement {
+        private final List<ExtractorStatement> statements;
+
+        public ExtractorBlock(final List<ExtractorStatement> statements) {
+            this.statements = Collections.unmodifiableList(statements);
+        }
+    }
+
+    public interface ExtractorStatement {
+    }
+
+    @Getter
+    public static final class FieldAssignment implements ExtractorStatement, FilterStatement {
+        private final FieldType fieldType;
+        private final ValueAccess value;
+        private final String castType;
+        private final String formatPattern;
+
+        public FieldAssignment(final FieldType fieldType,
+                               final ValueAccess value,
+                               final String castType,
+                               final String formatPattern) {
+            this.fieldType = fieldType;
+            this.value = value;
+            this.castType = castType;
+            this.formatPattern = formatPattern;
+        }
+    }
+
+    public enum FieldType {
+        SERVICE, INSTANCE, ENDPOINT, LAYER,
+        TRACE_ID, SEGMENT_ID, SPAN_ID, TIMESTAMP
+    }
+
+    @Getter
+    public static final class TagAssignment implements ExtractorStatement, FilterStatement {
+        private final Map<String, TagValue> tags;
+
+        public TagAssignment(final Map<String, TagValue> tags) {
+            this.tags = Collections.unmodifiableMap(tags);
+        }
+    }
+
+    @Getter
+    public static final class TagValue {
+        private final ValueAccess value;
+        private final String castType;
+
+        public TagValue(final ValueAccess value, final String castType) {
+            this.value = value;
+            this.castType = castType;
+        }
+    }
+
+    @Getter
+    public static final class MetricsBlock implements ExtractorStatement, FilterStatement {
+        private final String name;
+        private final ValueAccess timestampValue;
+        private final String timestampCast;
+        private final Map<String, TagValue> labels;
+        private final ValueAccess value;
+        private final String valueCast;
+
+        public MetricsBlock(final String name,
+                            final ValueAccess timestampValue,
+                            final String timestampCast,
+                            final Map<String, TagValue> labels,
+                            final ValueAccess value,
+                            final String valueCast) {
+            this.name = name;
+            this.timestampValue = timestampValue;
+            this.timestampCast = timestampCast;
+            this.labels = labels != null ? Collections.unmodifiableMap(labels) : Collections.emptyMap();
+            this.value = value;
+            this.valueCast = valueCast;
+        }
+    }
+
+    @Getter
+    public static final class SlowSqlBlock implements ExtractorStatement, FilterStatement {
+        private final ValueAccess id;
+        private final String idCast;
+        private final ValueAccess statement;
+        private final String statementCast;
+        private final ValueAccess latency;
+        private final String latencyCast;
+
+        public SlowSqlBlock(final ValueAccess id, final String idCast,
+                            final ValueAccess statement, final String statementCast,
+                            final ValueAccess latency, final String latencyCast) {
+            this.id = id;
+            this.idCast = idCast;
+            this.statement = statement;
+            this.statementCast = statementCast;
+            this.latency = latency;
+            this.latencyCast = latencyCast;
+        }
+    }
+
+    @Getter
+    public static final class SampledTraceBlock implements ExtractorStatement, FilterStatement {
+        private final List<SampledTraceStatement> statements;
+
+        public SampledTraceBlock(final List<SampledTraceStatement> statements) {
+            this.statements = Collections.unmodifiableList(statements);
+        }
+    }
+
+    public interface SampledTraceStatement {
+    }
+
+    @Getter
+    public static final class SampledTraceField implements SampledTraceStatement {
+        private final SampledTraceFieldType fieldType;
+        private final ValueAccess value;
+        private final String castType;
+
+        public SampledTraceField(final SampledTraceFieldType fieldType,
+                                 final ValueAccess value,
+                                 final String castType) {
+            this.fieldType = fieldType;
+            this.value = value;
+            this.castType = castType;
+        }
+    }
+
+    public enum SampledTraceFieldType {
+        LATENCY, URI, REASON, PROCESS_ID, DEST_PROCESS_ID,
+        DETECT_POINT, COMPONENT_ID, REPORT_SERVICE
+    }
+
+    // ==================== Sink block ====================
+
+    @Getter
+    public static final class SinkBlock implements FilterStatement {
+        private final List<SinkStatement> statements;
+
+        public SinkBlock(final List<SinkStatement> statements) {
+            this.statements = Collections.unmodifiableList(statements);
+        }
+    }
+
+    public interface SinkStatement {
+    }
+
+    @Getter
+    public static final class SamplerBlock implements SinkStatement, FilterStatement {
+        private final List<SamplerContent> contents;
+
+        public SamplerBlock(final List<SamplerContent> contents) {
+            this.contents = Collections.unmodifiableList(contents);
+        }
+    }
+
+    public interface SamplerContent {
+    }
+
+    @Getter
+    public static final class RateLimitBlock implements SamplerContent {
+        private final String id;
+        private final long rpm;
+
+        public RateLimitBlock(final String id, final long rpm) {
+            this.id = id;
+            this.rpm = rpm;
+        }
+    }
+
+    public static final class EnforcerStatement implements SinkStatement, FilterStatement {
+    }
+
+    public static final class DropperStatement implements SinkStatement, FilterStatement {
+    }
+
+    // ==================== Control flow ====================
+
+    @Getter
+    public static final class IfBlock implements FilterStatement, ExtractorStatement,
+            SinkStatement, SampledTraceStatement, SamplerContent {
+        private final Condition condition;
+        private final List<FilterStatement> thenBranch;
+        private final List<FilterStatement> elseBranch;
+
+        public IfBlock(final Condition condition,
+                       final List<FilterStatement> thenBranch,
+                       final List<FilterStatement> elseBranch) {
+            this.condition = condition;
+            this.thenBranch = Collections.unmodifiableList(thenBranch);
+            this.elseBranch = elseBranch != null
+                ? Collections.unmodifiableList(elseBranch) : Collections.emptyList();
+        }
+    }
+
+    // ==================== Conditions ====================
+
+    public interface Condition {
+    }
+
+    @Getter
+    public static final class ComparisonCondition implements Condition {
+        private final ValueAccess left;
+        private final String leftCast;
+        private final CompareOp op;
+        private final ConditionValue right;
+
+        public ComparisonCondition(final ValueAccess left,
+                                   final String leftCast,
+                                   final CompareOp op,
+                                   final ConditionValue right) {
+            this.left = left;
+            this.leftCast = leftCast;
+            this.op = op;
+            this.right = right;
+        }
+    }
+
+    @Getter
+    public static final class LogicalCondition implements Condition {
+        private final Condition left;
+        private final LogicalOp op;
+        private final Condition right;
+
+        public LogicalCondition(final Condition left, final LogicalOp op, final Condition right) {
+            this.left = left;
+            this.op = op;
+            this.right = right;
+        }
+    }
+
+    @Getter
+    public static final class NotCondition implements Condition {
+        private final Condition inner;
+
+        public NotCondition(final Condition inner) {
+            this.inner = inner;
+        }
+    }
+
+    @Getter
+    public static final class ExprCondition implements Condition {
+        private final ValueAccess expr;
+        private final String castType;
+
+        public ExprCondition(final ValueAccess expr, final String castType) {
+            this.expr = expr;
+            this.castType = castType;
+        }
+    }
+
+    // ==================== Value access ====================
+
+    @Getter
+    public static final class ValueAccess {
+        private final List<String> segments;
+        private final boolean parsedRef;
+        private final boolean logRef;
+        private final List<ValueAccessSegment> chain;
+
+        public ValueAccess(final List<String> segments,
+                           final boolean parsedRef,
+                           final boolean logRef,
+                           final List<ValueAccessSegment> chain) {
+            this.segments = Collections.unmodifiableList(segments);
+            this.parsedRef = parsedRef;
+            this.logRef = logRef;
+            this.chain = chain != null
+                ? Collections.unmodifiableList(chain) : Collections.emptyList();
+        }
+
+        public String toPathString() {
+            return String.join(".", segments);
+        }
+    }
+
+    public interface ValueAccessSegment {
+    }
+
+    @Getter
+    public static final class FieldSegment implements ValueAccessSegment {
+        private final String name;
+        private final boolean safeNav;
+
+        public FieldSegment(final String name, final boolean safeNav) {
+            this.name = name;
+            this.safeNav = safeNav;
+        }
+    }
+
+    @Getter
+    public static final class MethodSegment implements ValueAccessSegment {
+        private final String name;
+        private final List<String> arguments;
+        private final boolean safeNav;
+
+        public MethodSegment(final String name, final List<String> arguments, final boolean safeNav) {
+            this.name = name;
+            this.arguments = Collections.unmodifiableList(arguments);
+            this.safeNav = safeNav;
+        }
+    }
+
+    // ==================== Condition values ====================
+
+    public interface ConditionValue {
+    }
+
+    @Getter
+    public static final class StringConditionValue implements ConditionValue {
+        private final String value;
+
+        public StringConditionValue(final String value) {
+            this.value = value;
+        }
+    }
+
+    @Getter
+    public static final class NumberConditionValue implements ConditionValue {
+        private final double value;
+
+        public NumberConditionValue(final double value) {
+            this.value = value;
+        }
+    }
+
+    @Getter
+    public static final class BoolConditionValue implements ConditionValue {
+        private final boolean value;
+
+        public BoolConditionValue(final boolean value) {
+            this.value = value;
+        }
+    }
+
+    public static final class NullConditionValue implements ConditionValue {
+    }
+
+    @Getter
+    public static final class ValueAccessConditionValue implements ConditionValue {
+        private final ValueAccess value;
+        private final String castType;
+
+        public ValueAccessConditionValue(final ValueAccess value, final String castType) {
+            this.value = value;
+            this.castType = castType;
+        }
+    }
+
+    @Getter
+    public static final class FunctionCallConditionValue implements ConditionValue {
+        private final String functionName;
+        private final List<String> arguments;
+
+        public FunctionCallConditionValue(final String functionName, final List<String> arguments) {
+            this.functionName = functionName;
+            this.arguments = Collections.unmodifiableList(arguments);
+        }
+    }
+
+    // ==================== Enums ====================
+
+    public enum CompareOp {
+        EQ, NEQ, GT, LT, GTE, LTE
+    }
+
+    public enum LogicalOp {
+        AND, OR
+    }
+
+    private LALScriptModel() {
+        this.statements = Collections.emptyList();
+    }
+}
diff --git a/oap-server/analyzer/log-analyzer/src/main/java/org/apache/skywalking/oap/log/analyzer/compiler/LALScriptParser.java b/oap-server/analyzer/log-analyzer/src/main/java/org/apache/skywalking/oap/log/analyzer/compiler/LALScriptParser.java
new file mode 100644
index 0000000000..128f325573
--- /dev/null
+++ b/oap-server/analyzer/log-analyzer/src/main/java/org/apache/skywalking/oap/log/analyzer/compiler/LALScriptParser.java
@@ -0,0 +1,687 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.skywalking.oap.log.analyzer.compiler;
+
+import java.util.ArrayList;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import org.antlr.v4.runtime.BaseErrorListener;
+import org.antlr.v4.runtime.CharStreams;
+import org.antlr.v4.runtime.CommonTokenStream;
+import org.antlr.v4.runtime.RecognitionException;
+import org.antlr.v4.runtime.Recognizer;
+import org.apache.skywalking.lal.rt.grammar.LALLexer;
+import org.apache.skywalking.lal.rt.grammar.LALParser;
+import org.apache.skywalking.oap.log.analyzer.compiler.LALScriptModel.AbortStatement;
+import org.apache.skywalking.oap.log.analyzer.compiler.LALScriptModel.CompareOp;
+import org.apache.skywalking.oap.log.analyzer.compiler.LALScriptModel.ComparisonCondition;
+import org.apache.skywalking.oap.log.analyzer.compiler.LALScriptModel.Condition;
+import org.apache.skywalking.oap.log.analyzer.compiler.LALScriptModel.DropperStatement;
+import org.apache.skywalking.oap.log.analyzer.compiler.LALScriptModel.EnforcerStatement;
+import org.apache.skywalking.oap.log.analyzer.compiler.LALScriptModel.ExprCondition;
+import org.apache.skywalking.oap.log.analyzer.compiler.LALScriptModel.ExtractorBlock;
+import org.apache.skywalking.oap.log.analyzer.compiler.LALScriptModel.ExtractorStatement;
+import org.apache.skywalking.oap.log.analyzer.compiler.LALScriptModel.FieldAssignment;
+import org.apache.skywalking.oap.log.analyzer.compiler.LALScriptModel.FieldSegment;
+import org.apache.skywalking.oap.log.analyzer.compiler.LALScriptModel.FieldType;
+import org.apache.skywalking.oap.log.analyzer.compiler.LALScriptModel.FilterStatement;
+import org.apache.skywalking.oap.log.analyzer.compiler.LALScriptModel.IfBlock;
+import org.apache.skywalking.oap.log.analyzer.compiler.LALScriptModel.JsonParser;
+import org.apache.skywalking.oap.log.analyzer.compiler.LALScriptModel.LogicalCondition;
+import org.apache.skywalking.oap.log.analyzer.compiler.LALScriptModel.LogicalOp;
+import org.apache.skywalking.oap.log.analyzer.compiler.LALScriptModel.MetricsBlock;
+import org.apache.skywalking.oap.log.analyzer.compiler.LALScriptModel.NotCondition;
+import org.apache.skywalking.oap.log.analyzer.compiler.LALScriptModel.NullConditionValue;
+import org.apache.skywalking.oap.log.analyzer.compiler.LALScriptModel.NumberConditionValue;
+import org.apache.skywalking.oap.log.analyzer.compiler.LALScriptModel.RateLimitBlock;
+import org.apache.skywalking.oap.log.analyzer.compiler.LALScriptModel.SamplerBlock;
+import org.apache.skywalking.oap.log.analyzer.compiler.LALScriptModel.SamplerContent;
+import org.apache.skywalking.oap.log.analyzer.compiler.LALScriptModel.SinkBlock;
+import org.apache.skywalking.oap.log.analyzer.compiler.LALScriptModel.SinkStatement;
+import org.apache.skywalking.oap.log.analyzer.compiler.LALScriptModel.SlowSqlBlock;
+import org.apache.skywalking.oap.log.analyzer.compiler.LALScriptModel.StringConditionValue;
+import org.apache.skywalking.oap.log.analyzer.compiler.LALScriptModel.TagAssignment;
+import org.apache.skywalking.oap.log.analyzer.compiler.LALScriptModel.TagValue;
+import org.apache.skywalking.oap.log.analyzer.compiler.LALScriptModel.TextParser;
+import org.apache.skywalking.oap.log.analyzer.compiler.LALScriptModel.ValueAccess;
+import org.apache.skywalking.oap.log.analyzer.compiler.LALScriptModel.ValueAccessConditionValue;
+import org.apache.skywalking.oap.log.analyzer.compiler.LALScriptModel.ValueAccessSegment;
+import org.apache.skywalking.oap.log.analyzer.compiler.LALScriptModel.YamlParser;
+
+/**
+ * Facade: parses LAL DSL script strings into {@link LALScriptModel}.
+ *
+ * <pre>
+ *   LALScriptModel model = LALScriptParser.parse(
+ *       "filter { json {} extractor { service parsed.service as String } sink {} }");
+ * </pre>
+ */
+public final class LALScriptParser {
+
+    private LALScriptParser() {
+    }
+
+    public static LALScriptModel parse(final String dsl) {
+        final LALLexer lexer = new LALLexer(CharStreams.fromString(dsl));
+        final CommonTokenStream tokens = new CommonTokenStream(lexer);
+        final LALParser parser = new LALParser(tokens);
+
+        final List<String> errors = new ArrayList<>();
+        parser.removeErrorListeners();
+        parser.addErrorListener(new BaseErrorListener() {
+            @Override
+            public void syntaxError(final Recognizer<?, ?> recognizer,
+                                    final Object offendingSymbol,
+                                    final int line,
+                                    final int charPositionInLine,
+                                    final String msg,
+                                    final RecognitionException e) {
+                errors.add(line + ":" + charPositionInLine + " " + msg);
+            }
+        });
+
+        final LALParser.RootContext tree = parser.root();
+        if (!errors.isEmpty()) {
+            throw new IllegalArgumentException(
+                "LAL script parsing failed: " + String.join("; ", errors)
+                    + " in script: " + truncate(dsl, 200));
+        }
+
+        final List<FilterStatement> stmts = visitFilterContent(
+            tree.filterBlock().filterContent());
+        return new LALScriptModel(stmts);
+    }
+
+    // ==================== Filter content ====================
+
+    private static List<FilterStatement> visitFilterContent(
+            final LALParser.FilterContentContext ctx) {
+        final List<FilterStatement> stmts = new ArrayList<>();
+        for (final LALParser.FilterStatementContext fsc : ctx.filterStatement()) {
+            stmts.add(visitFilterStatement(fsc));
+        }
+        return stmts;
+    }
+
+    private static FilterStatement visitFilterStatement(
+            final LALParser.FilterStatementContext ctx) {
+        if (ctx.parserBlock() != null) {
+            return visitParserBlock(ctx.parserBlock());
+        }
+        if (ctx.extractorBlock() != null) {
+            return visitExtractorBlock(ctx.extractorBlock());
+        }
+        if (ctx.sinkBlock() != null) {
+            return visitSinkBlock(ctx.sinkBlock());
+        }
+        if (ctx.abortBlock() != null) {
+            return new AbortStatement();
+        }
+        // ifStatement
+        return visitIfStatement(ctx.ifStatement());
+    }
+
+    // ==================== Parser blocks ====================
+
+    private static FilterStatement visitParserBlock(final LALParser.ParserBlockContext ctx) {
+        if (ctx.textBlock() != null) {
+            String pattern = null;
+            boolean abortOnFail = false;
+            if (ctx.textBlock().textContent() != null) {
+                for (final LALParser.RegexpStatementContext regCtx :
+                        ctx.textBlock().textContent().regexpStatement()) {
+                    pattern = stripQuotes(regCtx.regexpPattern().getText());
+                }
+                for (final LALParser.AbortOnFailureStatementContext abfCtx :
+                        ctx.textBlock().textContent().abortOnFailureStatement()) {
+                    abortOnFail = "true".equals(abfCtx.boolValue().getText());
+                }
+            }
+            return new TextParser(pattern, abortOnFail);
+        }
+        if (ctx.jsonBlock() != null) {
+            boolean abortOnFail = false;
+            if (ctx.jsonBlock().jsonContent() != null
+                    && ctx.jsonBlock().jsonContent().abortOnFailureStatement() != null) {
+                abortOnFail = "true".equals(
+                    ctx.jsonBlock().jsonContent().abortOnFailureStatement()
+                        .boolValue().getText());
+            }
+            return new JsonParser(abortOnFail);
+        }
+        // yaml
+        boolean abortOnFail = false;
+        if (ctx.yamlBlock().yamlContent() != null
+                && ctx.yamlBlock().yamlContent().abortOnFailureStatement() != null) {
+            abortOnFail = "true".equals(
+                ctx.yamlBlock().yamlContent().abortOnFailureStatement()
+                    .boolValue().getText());
+        }
+        return new YamlParser(abortOnFail);
+    }
+
+    // ==================== Extractor block ====================
+
+    private static ExtractorBlock visitExtractorBlock(
+            final LALParser.ExtractorBlockContext ctx) {
+        final List<ExtractorStatement> stmts = new ArrayList<>();
+        for (final LALParser.ExtractorStatementContext esc : ctx.extractorContent().extractorStatement()) {
+            stmts.add(visitExtractorStatement(esc));
+        }
+        return new ExtractorBlock(stmts);
+    }
+
+    private static ExtractorStatement visitExtractorStatement(
+            final LALParser.ExtractorStatementContext ctx) {
+        if (ctx.serviceStatement() != null) {
+            return visitFieldAssignment(FieldType.SERVICE, ctx.serviceStatement().valueAccess(),
+                ctx.serviceStatement().typeCast());
+        }
+        if (ctx.instanceStatement() != null) {
+            return visitFieldAssignment(FieldType.INSTANCE, ctx.instanceStatement().valueAccess(),
+                ctx.instanceStatement().typeCast());
+        }
+        if (ctx.endpointStatement() != null) {
+            return visitFieldAssignment(FieldType.ENDPOINT, ctx.endpointStatement().valueAccess(),
+                ctx.endpointStatement().typeCast());
+        }
+        if (ctx.layerStatement() != null) {
+            return visitFieldAssignment(FieldType.LAYER, ctx.layerStatement().valueAccess(),
+                ctx.layerStatement().typeCast());
+        }
+        if (ctx.traceIdStatement() != null) {
+            return visitFieldAssignment(FieldType.TRACE_ID, ctx.traceIdStatement().valueAccess(),
+                ctx.traceIdStatement().typeCast());
+        }
+        if (ctx.timestampStatement() != null) {
+            final ValueAccess va = visitValueAccess(ctx.timestampStatement().valueAccess());
+            final String cast = ctx.timestampStatement().typeCast() != null
+                ? extractCastType(ctx.timestampStatement().typeCast()) : null;
+            String format = null;
+            if (ctx.timestampStatement().STRING() != null) {
+                format = stripQuotes(ctx.timestampStatement().STRING().getText());
+            }
+            return new FieldAssignment(FieldType.TIMESTAMP, va, cast, format);
+        }
+        if (ctx.tagStatement() != null) {
+            return visitTagStatement(ctx.tagStatement());
+        }
+        if (ctx.metricsBlock() != null) {
+            return visitMetricsBlock(ctx.metricsBlock());
+        }
+        if (ctx.slowSqlBlock() != null) {
+            return visitSlowSqlBlock(ctx.slowSqlBlock());
+        }
+        if (ctx.sampledTraceBlock() != null) {
+            return visitSampledTraceBlock(ctx.sampledTraceBlock());
+        }
+        // ifStatement
+        return (ExtractorStatement) visitIfStatement(ctx.ifStatement());
+    }
+
+    private static FieldAssignment visitFieldAssignment(
+            final FieldType type,
+            final LALParser.ValueAccessContext vaCtx,
+            final LALParser.TypeCastContext tcCtx) {
+        final ValueAccess va = visitValueAccess(vaCtx);
+        final String cast = tcCtx != null ? extractCastType(tcCtx) : null;
+        return new FieldAssignment(type, va, cast, null);
+    }
+
+    // ==================== Tag statement ====================
+
+    private static TagAssignment visitTagStatement(final LALParser.TagStatementContext ctx) {
+        final Map<String, TagValue> tags = new LinkedHashMap<>();
+        if (ctx.tagMap() != null) {
+            for (int i = 0; i < ctx.tagMap().anyIdentifier().size(); i++) {
+                final String key = ctx.tagMap().anyIdentifier(i).getText();
+                final ValueAccess va = visitValueAccess(ctx.tagMap().valueAccess(i));
+                final String cast = ctx.tagMap().typeCast(i) != null
+                    ? extractCastType(ctx.tagMap().typeCast(i)) : null;
+                tags.put(key, new TagValue(va, cast));
+            }
+        } else if (ctx.STRING() != null) {
+            final String key = stripQuotes(ctx.STRING().getText());
+            final ValueAccess va = visitValueAccess(ctx.valueAccess());
+            final String cast = ctx.typeCast() != null ? extractCastType(ctx.typeCast()) : null;
+            tags.put(key, new TagValue(va, cast));
+        }
+        return new TagAssignment(tags);
+    }
+
+    // ==================== Metrics block ====================
+
+    private static MetricsBlock visitMetricsBlock(final LALParser.MetricsBlockContext ctx) {
+        String name = null;
+        ValueAccess timestampValue = null;
+        String timestampCast = null;
+        final Map<String, TagValue> labels = new LinkedHashMap<>();
+        ValueAccess value = null;
+        String valueCast = null;
+
+        for (final LALParser.MetricsStatementContext msc : ctx.metricsContent().metricsStatement()) {
+            if (msc.metricsNameStatement() != null) {
+                name = resolveValueAsString(msc.metricsNameStatement().valueAccess());
+            }
+            if (msc.metricsTimestampStatement() != null) {
+                timestampValue = visitValueAccess(msc.metricsTimestampStatement().valueAccess());
+                timestampCast = msc.metricsTimestampStatement().typeCast() != null
+                    ? extractCastType(msc.metricsTimestampStatement().typeCast()) : null;
+            }
+            if (msc.metricsLabelsStatement() != null) {
+                for (final LALParser.LabelEntryContext lec :
+                        msc.metricsLabelsStatement().labelMap().labelEntry()) {
+                    final String key = lec.anyIdentifier().getText();
+                    final ValueAccess va = visitValueAccess(lec.valueAccess());
+                    final String cast = lec.typeCast() != null
+                        ? extractCastType(lec.typeCast()) : null;
+                    labels.put(key, new TagValue(va, cast));
+                }
+            }
+            if (msc.metricsValueStatement() != null) {
+                value = visitValueAccess(msc.metricsValueStatement().valueAccess());
+                valueCast = msc.metricsValueStatement().typeCast() != null
+                    ? extractCastType(msc.metricsValueStatement().typeCast()) : null;
+            }
+        }
+
+        return new MetricsBlock(name, timestampValue, timestampCast, labels, value, valueCast);
+    }
+
+    // ==================== Slow SQL block ====================
+
+    private static SlowSqlBlock visitSlowSqlBlock(final LALParser.SlowSqlBlockContext ctx) {
+        ValueAccess id = null;
+        String idCast = null;
+        ValueAccess statement = null;
+        String statementCast = null;
+        ValueAccess latency = null;
+        String latencyCast = null;
+
+        for (final LALParser.SlowSqlStatementContext ssc :
+                ctx.slowSqlContent().slowSqlStatement()) {
+            if (ssc.slowSqlIdStatement() != null) {
+                id = visitValueAccess(ssc.slowSqlIdStatement().valueAccess());
+                idCast = ssc.slowSqlIdStatement().typeCast() != null
+                    ? extractCastType(ssc.slowSqlIdStatement().typeCast()) : null;
+            }
+            if (ssc.slowSqlStatementStatement() != null) {
+                statement = visitValueAccess(ssc.slowSqlStatementStatement().valueAccess());
+                statementCast = ssc.slowSqlStatementStatement().typeCast() != null
+                    ? extractCastType(ssc.slowSqlStatementStatement().typeCast()) : null;
+            }
+            if (ssc.slowSqlLatencyStatement() != null) {
+                latency = visitValueAccess(ssc.slowSqlLatencyStatement().valueAccess());
+                latencyCast = ssc.slowSqlLatencyStatement().typeCast() != null
+                    ? extractCastType(ssc.slowSqlLatencyStatement().typeCast()) : null;
+            }
+        }
+
+        return new SlowSqlBlock(id, idCast, statement, statementCast, latency, latencyCast);
+    }
+
+    // ==================== Sampled trace block ====================
+
+    private static LALScriptModel.SampledTraceBlock visitSampledTraceBlock(
+            final LALParser.SampledTraceBlockContext ctx) {
+        final List<LALScriptModel.SampledTraceStatement> stmts = new ArrayList<>();
+        for (final LALParser.SampledTraceStatementContext stc :
+                ctx.sampledTraceContent().sampledTraceStatement()) {
+            if (stc.ifStatement() != null) {
+                stmts.add((LALScriptModel.SampledTraceStatement) visitIfStatement(
+                    stc.ifStatement()));
+            } else {
+                stmts.add(visitSampledTraceField(stc));
+            }
+        }
+        return new LALScriptModel.SampledTraceBlock(stmts);
+    }
+
+    private static LALScriptModel.SampledTraceField visitSampledTraceField(
+            final LALParser.SampledTraceStatementContext ctx) {
+        if (ctx.sampledTraceLatencyStatement() != null) {
+            return makeSampledField(LALScriptModel.SampledTraceFieldType.LATENCY,
+                ctx.sampledTraceLatencyStatement().valueAccess(),
+                ctx.sampledTraceLatencyStatement().typeCast());
+        }
+        if (ctx.sampledTraceUriStatement() != null) {
+            return makeSampledField(LALScriptModel.SampledTraceFieldType.URI,
+                ctx.sampledTraceUriStatement().valueAccess(),
+                ctx.sampledTraceUriStatement().typeCast());
+        }
+        if (ctx.sampledTraceReasonStatement() != null) {
+            return makeSampledField(LALScriptModel.SampledTraceFieldType.REASON,
+                ctx.sampledTraceReasonStatement().valueAccess(),
+                ctx.sampledTraceReasonStatement().typeCast());
+        }
+        if (ctx.sampledTraceProcessIdStatement() != null) {
+            return makeSampledField(LALScriptModel.SampledTraceFieldType.PROCESS_ID,
+                ctx.sampledTraceProcessIdStatement().valueAccess(),
+                ctx.sampledTraceProcessIdStatement().typeCast());
+        }
+        if (ctx.sampledTraceDestProcessIdStatement() != null) {
+            return makeSampledField(LALScriptModel.SampledTraceFieldType.DEST_PROCESS_ID,
+                ctx.sampledTraceDestProcessIdStatement().valueAccess(),
+                ctx.sampledTraceDestProcessIdStatement().typeCast());
+        }
+        if (ctx.sampledTraceDetectPointStatement() != null) {
+            return makeSampledField(LALScriptModel.SampledTraceFieldType.DETECT_POINT,
+                ctx.sampledTraceDetectPointStatement().valueAccess(),
+                ctx.sampledTraceDetectPointStatement().typeCast());
+        }
+        if (ctx.sampledTraceComponentIdStatement() != null) {
+            return makeSampledField(LALScriptModel.SampledTraceFieldType.COMPONENT_ID,
+                ctx.sampledTraceComponentIdStatement().valueAccess(),
+                ctx.sampledTraceComponentIdStatement().typeCast());
+        }
+        // reportService
+        return makeSampledField(LALScriptModel.SampledTraceFieldType.REPORT_SERVICE,
+            ctx.reportServiceStatement().valueAccess(),
+            ctx.reportServiceStatement().typeCast());
+    }
+
+    private static LALScriptModel.SampledTraceField makeSampledField(
+            final LALScriptModel.SampledTraceFieldType type,
+            final LALParser.ValueAccessContext vaCtx,
+            final LALParser.TypeCastContext tcCtx) {
+        return new LALScriptModel.SampledTraceField(
+            type, visitValueAccess(vaCtx),
+            tcCtx != null ? extractCastType(tcCtx) : null);
+    }
+
+    // ==================== Sink block ====================
+
+    private static SinkBlock visitSinkBlock(final LALParser.SinkBlockContext ctx) {
+        final List<SinkStatement> stmts = new ArrayList<>();
+        for (final LALParser.SinkStatementContext ssc : ctx.sinkContent().sinkStatement()) {
+            if (ssc.samplerBlock() != null) {
+                stmts.add(visitSamplerBlock(ssc.samplerBlock()));
+            } else if (ssc.enforcerStatement() != null) {
+                stmts.add(new EnforcerStatement());
+            } else if (ssc.dropperStatement() != null) {
+                stmts.add(new DropperStatement());
+            } else {
+                stmts.add((SinkStatement) visitIfStatement(ssc.ifStatement()));
+            }
+        }
+        return new SinkBlock(stmts);
+    }
+
+    private static SamplerBlock visitSamplerBlock(final LALParser.SamplerBlockContext ctx) {
+        final List<SamplerContent> contents = new ArrayList<>();
+        for (final LALParser.RateLimitBlockContext rlc : ctx.samplerContent().rateLimitBlock()) {
+            final String id = stripQuotes(rlc.rateLimitId().getText());
+            final long rpm = Long.parseLong(rlc.rateLimitContent().NUMBER().getText());
+            contents.add(new RateLimitBlock(id, rpm));
+        }
+        for (final LALParser.IfStatementContext isc : ctx.samplerContent().ifStatement()) {
+            contents.add((SamplerContent) visitIfStatement(isc));
+        }
+        return new SamplerBlock(contents);
+    }
+
+    // ==================== If statement ====================
+
+    private static IfBlock visitIfStatement(final LALParser.IfStatementContext ctx) {
+        final Condition condition = visitCondition(ctx.condition(0));
+        final List<FilterStatement> thenBranch = visitIfBody(ctx.ifBody(0));
+
+        List<FilterStatement> elseBranch = null;
+        // Handle else-if and else branches
+        final int bodyCount = ctx.ifBody().size();
+        if (bodyCount > 1) {
+            elseBranch = visitIfBody(ctx.ifBody(bodyCount - 1));
+        }
+
+        return new IfBlock(condition, thenBranch, elseBranch);
+    }
+
+    private static List<FilterStatement> visitIfBody(final LALParser.IfBodyContext ctx) {
+        final List<FilterStatement> stmts = new ArrayList<>();
+        for (final LALParser.FilterStatementContext fsc : ctx.filterStatement()) {
+            stmts.add(visitFilterStatement(fsc));
+        }
+        for (final LALParser.ExtractorStatementContext esc : ctx.extractorStatement()) {
+            stmts.add((FilterStatement) visitExtractorStatement(esc));
+        }
+        for (final LALParser.SinkStatementContext ssc : ctx.sinkStatement()) {
+            if (ssc.samplerBlock() != null) {
+                stmts.add((FilterStatement) visitSamplerBlock(ssc.samplerBlock()));
+            } else if (ssc.enforcerStatement() != null) {
+                stmts.add((FilterStatement) new EnforcerStatement());
+            } else if (ssc.dropperStatement() != null) {
+                stmts.add((FilterStatement) new DropperStatement());
+            }
+        }
+        return stmts;
+    }
+
+    // ==================== Conditions ====================
+
+    private static Condition visitCondition(final LALParser.ConditionContext ctx) {
+        if (ctx instanceof LALParser.CondAndContext) {
+            final LALParser.CondAndContext and = (LALParser.CondAndContext) ctx;
+            return new LogicalCondition(
+                visitCondition(and.condition(0)),
+                LogicalOp.AND,
+                visitCondition(and.condition(1)));
+        }
+        if (ctx instanceof LALParser.CondOrContext) {
+            final LALParser.CondOrContext or = (LALParser.CondOrContext) ctx;
+            return new LogicalCondition(
+                visitCondition(or.condition(0)),
+                LogicalOp.OR,
+                visitCondition(or.condition(1)));
+        }
+        if (ctx instanceof LALParser.CondNotContext) {
+            return new NotCondition(
+                visitCondition(((LALParser.CondNotContext) ctx).condition()));
+        }
+        if (ctx instanceof LALParser.CondParenContext) {
+            return visitCondition(((LALParser.CondParenContext) ctx).condition());
+        }
+        if (ctx instanceof LALParser.CondEqContext) {
+            final LALParser.CondEqContext eq = (LALParser.CondEqContext) ctx;
+            return makeComparison(eq.conditionExpr(0), CompareOp.EQ, eq.conditionExpr(1));
+        }
+        if (ctx instanceof LALParser.CondNeqContext) {
+            final LALParser.CondNeqContext neq = (LALParser.CondNeqContext) ctx;
+            return makeComparison(neq.conditionExpr(0), CompareOp.NEQ, neq.conditionExpr(1));
+        }
+        if (ctx instanceof LALParser.CondGtContext) {
+            final LALParser.CondGtContext gt = (LALParser.CondGtContext) ctx;
+            return makeComparison(gt.conditionExpr(0), CompareOp.GT, gt.conditionExpr(1));
+        }
+        if (ctx instanceof LALParser.CondLtContext) {
+            final LALParser.CondLtContext lt = (LALParser.CondLtContext) ctx;
+            return makeComparison(lt.conditionExpr(0), CompareOp.LT, lt.conditionExpr(1));
+        }
+        if (ctx instanceof LALParser.CondGteContext) {
+            final LALParser.CondGteContext gte = (LALParser.CondGteContext) ctx;
+            return makeComparison(gte.conditionExpr(0), CompareOp.GTE, gte.conditionExpr(1));
+        }
+        if (ctx instanceof LALParser.CondLteContext) {
+            final LALParser.CondLteContext lte = (LALParser.CondLteContext) ctx;
+            return makeComparison(lte.conditionExpr(0), CompareOp.LTE, lte.conditionExpr(1));
+        }
+        // condSingle
+        final LALParser.CondSingleContext single = (LALParser.CondSingleContext) ctx;
+        return visitConditionExprAsCondition(single.conditionExpr());
+    }
+
+    private static Condition makeComparison(
+            final LALParser.ConditionExprContext leftCtx,
+            final CompareOp op,
+            final LALParser.ConditionExprContext rightCtx) {
+        if (leftCtx instanceof LALParser.CondValueAccessContext) {
+            final LALParser.CondValueAccessContext lva =
+                (LALParser.CondValueAccessContext) leftCtx;
+            final ValueAccess left = visitValueAccess(lva.valueAccess());
+            final String leftCast = lva.typeCast() != null
+                ? extractCastType(lva.typeCast()) : null;
+            return new ComparisonCondition(left, leftCast, op,
+                visitConditionExprAsValue(rightCtx));
+        }
+        // For function calls and other forms, wrap as expression condition
+        return new ExprCondition(
+            new ValueAccess(List.of(leftCtx.getText()), false, false, List.of()), null);
+    }
+
+    private static LALScriptModel.ConditionValue visitConditionExprAsValue(
+            final LALParser.ConditionExprContext ctx) {
+        if (ctx instanceof LALParser.CondStringContext) {
+            return new StringConditionValue(
+                stripQuotes(((LALParser.CondStringContext) ctx).STRING().getText()));
+        }
+        if (ctx instanceof LALParser.CondNumberContext) {
+            return new NumberConditionValue(
+                Double.parseDouble(((LALParser.CondNumberContext) ctx).NUMBER().getText()));
+        }
+        if (ctx instanceof LALParser.CondNullContext) {
+            return new NullConditionValue();
+        }
+        if (ctx instanceof LALParser.CondValueAccessContext) {
+            final LALParser.CondValueAccessContext va =
+                (LALParser.CondValueAccessContext) ctx;
+            final String cast = va.typeCast() != null ? extractCastType(va.typeCast()) : null;
+            return new ValueAccessConditionValue(visitValueAccess(va.valueAccess()), cast);
+        }
+        // condBool, condFunctionCall
+        return new StringConditionValue(ctx.getText());
+    }
+
+    private static Condition visitConditionExprAsCondition(
+            final LALParser.ConditionExprContext ctx) {
+        if (ctx instanceof LALParser.CondValueAccessContext) {
+            final LALParser.CondValueAccessContext va =
+                (LALParser.CondValueAccessContext) ctx;
+            final String cast = va.typeCast() != null ? extractCastType(va.typeCast()) : null;
+            return new ExprCondition(visitValueAccess(va.valueAccess()), cast);
+        }
+        return new ExprCondition(
+            new ValueAccess(List.of(ctx.getText()), false, false, List.of()), null);
+    }
+
+    // ==================== Value access ====================
+
+    private static ValueAccess visitValueAccess(final LALParser.ValueAccessContext ctx) {
+        final List<String> segments = new ArrayList<>();
+        boolean parsedRef = false;
+        boolean logRef = false;
+
+        final LALParser.ValueAccessPrimaryContext primary = ctx.valueAccessPrimary();
+        if (primary instanceof LALParser.ValueParsedContext) {
+            parsedRef = true;
+            segments.add("parsed");
+        } else if (primary instanceof LALParser.ValueLogContext) {
+            logRef = true;
+            segments.add("log");
+        } else if (primary instanceof LALParser.ValueIdentifierContext) {
+            segments.add(((LALParser.ValueIdentifierContext) primary).IDENTIFIER().getText());
+        } else if (primary instanceof LALParser.ValueStringContext) {
+            segments.add(stripQuotes(
+                ((LALParser.ValueStringContext) primary).STRING().getText()));
+        } else if (primary instanceof LALParser.ValueNumberContext) {
+            segments.add(((LALParser.ValueNumberContext) primary).NUMBER().getText());
+        } else if (primary instanceof LALParser.ValueFunctionCallContext) {
+            final LALParser.FunctionInvocationContext fi =
+                ((LALParser.ValueFunctionCallContext) primary).functionInvocation();
+            segments.add(fi.getText());
+        } else {
+            segments.add(primary.getText());
+        }
+
+        final List<ValueAccessSegment> chain = new ArrayList<>();
+        for (final LALParser.ValueAccessSegmentContext seg : ctx.valueAccessSegment()) {
+            if (seg instanceof LALParser.SegmentFieldContext) {
+                final String name =
+                    ((LALParser.SegmentFieldContext) seg).anyIdentifier().getText();
+                segments.add(name);
+                chain.add(new FieldSegment(name, false));
+            } else if (seg instanceof LALParser.SegmentSafeFieldContext) {
+                final String name =
+                    ((LALParser.SegmentSafeFieldContext) seg).anyIdentifier().getText();
+                segments.add(name);
+                chain.add(new FieldSegment(name, true));
+            } else if (seg instanceof LALParser.SegmentMethodContext) {
+                final LALParser.FunctionInvocationContext fi =
+                    ((LALParser.SegmentMethodContext) seg).functionInvocation();
+                segments.add(fi.IDENTIFIER().getText() + "()");
+                chain.add(new LALScriptModel.MethodSegment(
+                    fi.IDENTIFIER().getText(), List.of(), false));
+            } else if (seg instanceof LALParser.SegmentSafeMethodContext) {
+                final LALParser.FunctionInvocationContext fi =
+                    ((LALParser.SegmentSafeMethodContext) seg).functionInvocation();
+                segments.add(fi.IDENTIFIER().getText() + "()");
+                chain.add(new LALScriptModel.MethodSegment(
+                    fi.IDENTIFIER().getText(), List.of(), true));
+            }
+        }
+
+        return new ValueAccess(segments, parsedRef, logRef, chain);
+    }
+
+    private static String resolveValueAsString(final LALParser.ValueAccessContext ctx) {
+        final LALParser.ValueAccessPrimaryContext primary = ctx.valueAccessPrimary();
+        if (primary instanceof LALParser.ValueStringContext) {
+            return stripQuotes(((LALParser.ValueStringContext) primary).STRING().getText());
+        }
+        return primary.getText();
+    }
+
+    // ==================== Utilities ====================
+
+    private static String extractCastType(final LALParser.TypeCastContext ctx) {
+        if (ctx.STRING_TYPE() != null) {
+            return "String";
+        }
+        if (ctx.LONG_TYPE() != null) {
+            return "Long";
+        }
+        if (ctx.INTEGER_TYPE() != null) {
+            return "Integer";
+        }
+        if (ctx.BOOLEAN_TYPE() != null) {
+            return "Boolean";
+        }
+        return null;
+    }
+
+    static String stripQuotes(final String s) {
+        if (s == null || s.length() < 2) {
+            return s;
+        }
+        final char first = s.charAt(0);
+        if ((first == '\'' || first == '"') && s.charAt(s.length() - 1) == first) {
+            return s.substring(1, s.length() - 1);
+        }
+        // Handle slashy strings: $/ ... /$
+        if (s.startsWith("$/") && s.endsWith("/$")) {
+            return s.substring(2, s.length() - 2);
+        }
+        return s;
+    }
+
+    private static String truncate(final String s, final int maxLen) {
+        if (s.length() <= maxLen) {
+            return s;
+        }
+        return s.substring(0, maxLen) + "...";
+    }
+}
diff --git a/oap-server/analyzer/meter-analyzer/src/main/java/org/apache/skywalking/oap/meter/analyzer/dsl/MalExpression.java b/oap-server/analyzer/log-analyzer/src/main/java/org/apache/skywalking/oap/log/analyzer/compiler/rt/BindingAware.java
similarity index 71%
copy from oap-server/analyzer/meter-analyzer/src/main/java/org/apache/skywalking/oap/meter/analyzer/dsl/MalExpression.java
copy to oap-server/analyzer/log-analyzer/src/main/java/org/apache/skywalking/oap/log/analyzer/compiler/rt/BindingAware.java
index cfaf1d5bee..628e7f4874 100644
--- a/oap-server/analyzer/meter-analyzer/src/main/java/org/apache/skywalking/oap/meter/analyzer/dsl/MalExpression.java
+++ b/oap-server/analyzer/log-analyzer/src/main/java/org/apache/skywalking/oap/log/analyzer/compiler/rt/BindingAware.java
@@ -13,18 +13,19 @@
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
  * limitations under the License.
- *
  */
 
-package org.apache.skywalking.oap.meter.analyzer.dsl;
+package org.apache.skywalking.oap.log.analyzer.compiler.rt;
 
-import java.util.Map;
+import org.apache.skywalking.oap.log.analyzer.dsl.Binding;
 
 /**
- * Pure Java replacement for Groovy-based MAL DelegatingScript.
- * Each transpiled MAL expression implements this interface.
+ * Interface for generated consumer classes that need access to the
+ * current {@link Binding} at runtime.
  */
-@FunctionalInterface
-public interface MalExpression {
-    SampleFamily run(Map<String, SampleFamily> samples);
+public interface BindingAware {
+
+    void setBinding(Binding binding);
+
+    Binding getBinding();
 }
diff --git a/oap-server/analyzer/meter-analyzer/src/main/java/org/apache/skywalking/oap/meter/analyzer/dsl/MalExpression.java b/oap-server/analyzer/log-analyzer/src/main/java/org/apache/skywalking/oap/log/analyzer/compiler/rt/LalExpressionPackageHolder.java
similarity index 72%
copy from oap-server/analyzer/meter-analyzer/src/main/java/org/apache/skywalking/oap/meter/analyzer/dsl/MalExpression.java
copy to oap-server/analyzer/log-analyzer/src/main/java/org/apache/skywalking/oap/log/analyzer/compiler/rt/LalExpressionPackageHolder.java
index cfaf1d5bee..815284cf2b 100644
--- a/oap-server/analyzer/meter-analyzer/src/main/java/org/apache/skywalking/oap/meter/analyzer/dsl/MalExpression.java
+++ b/oap-server/analyzer/log-analyzer/src/main/java/org/apache/skywalking/oap/log/analyzer/compiler/rt/LalExpressionPackageHolder.java
@@ -13,18 +13,14 @@
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
  * limitations under the License.
- *
  */
 
-package org.apache.skywalking.oap.meter.analyzer.dsl;
-
-import java.util.Map;
+package org.apache.skywalking.oap.log.analyzer.compiler.rt;
 
 /**
- * Pure Java replacement for Groovy-based MAL DelegatingScript.
- * Each transpiled MAL expression implements this interface.
+ * Empty marker class used as the class loading anchor for Javassist
+ * {@code CtClass.toClass(Class)} on JDK 16+.
+ * Generated LAL expression classes are loaded in this package.
  */
-@FunctionalInterface
-public interface MalExpression {
-    SampleFamily run(Map<String, SampleFamily> samples);
+public class LalExpressionPackageHolder {
 }
diff --git a/oap-server/analyzer/log-analyzer/src/main/java/org/apache/skywalking/oap/log/analyzer/dsl/Binding.java b/oap-server/analyzer/log-analyzer/src/main/java/org/apache/skywalking/oap/log/analyzer/dsl/Binding.java
index 763dd64d28..ee2beaa474 100644
--- a/oap-server/analyzer/log-analyzer/src/main/java/org/apache/skywalking/oap/log/analyzer/dsl/Binding.java
+++ b/oap-server/analyzer/log-analyzer/src/main/java/org/apache/skywalking/oap/log/analyzer/dsl/Binding.java
@@ -13,15 +13,13 @@
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
  * limitations under the License.
- *
  */
 
 package org.apache.skywalking.oap.log.analyzer.dsl;
 
+import com.google.protobuf.Descriptors;
 import com.google.protobuf.Message;
-import groovy.lang.Closure;
-import groovy.lang.GroovyObjectSupport;
-import groovy.lang.MissingPropertyException;
+import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.Optional;
@@ -31,35 +29,38 @@ import lombok.Getter;
 import org.apache.skywalking.apm.network.logging.v3.LogData;
 import org.apache.skywalking.oap.meter.analyzer.dsl.SampleFamily;
 import org.apache.skywalking.oap.server.analyzer.provider.trace.parser.listener.DatabaseSlowStatementBuilder;
-
 import org.apache.skywalking.oap.server.analyzer.provider.trace.parser.listener.SampledTraceBuilder;
 import org.apache.skywalking.oap.server.core.source.Log;
 
 /**
- * The binding bridge between OAP and the DSL, which provides some convenient methods to ease the use of the raw {@link groovy.lang.Binding#setProperty(java.lang.String, java.lang.Object)} and {@link
- * groovy.lang.Binding#getProperty(java.lang.String)}.
+ * Same-FQCN replacement for upstream Binding.
+ * Pure Java implementation that does not extend groovy.lang.Binding.
+ * Uses a simple HashMap for property storage instead of Groovy's binding mechanism.
  */
-public class Binding extends groovy.lang.Binding {
+public class Binding {
     public static final String KEY_LOG = "log";
-
     public static final String KEY_PARSED = "parsed";
-
     public static final String KEY_SAVE = "save";
-
     public static final String KEY_ABORT = "abort";
-
     public static final String KEY_METRICS_CONTAINER = "metrics_container";
-
     public static final String KEY_LOG_CONTAINER = "log_container";
-
     public static final String KEY_DATABASE_SLOW_STATEMENT = "database_slow_statement";
-
     public static final String KEY_SAMPLED_TRACE = "sampled_trace";
 
+    private final Map<String, Object> properties = new HashMap<>();
+
     public Binding() {
         setProperty(KEY_PARSED, new Parsed());
     }
 
+    public void setProperty(final String name, final Object value) {
+        properties.put(name, value);
+    }
+
+    public Object getProperty(final String name) {
+        return properties.get(name);
+    }
+
     public Binding log(final LogData.Builder log) {
         setProperty(KEY_LOG, log);
         setProperty(KEY_SAVE, true);
@@ -105,7 +106,7 @@ public class Binding extends groovy.lang.Binding {
         return (DatabaseSlowStatementBuilder) getProperty(KEY_DATABASE_SLOW_STATEMENT);
     }
 
-    public Binding databaseSlowStatement(DatabaseSlowStatementBuilder databaseSlowStatementBuilder) {
+    public Binding databaseSlowStatement(final DatabaseSlowStatementBuilder databaseSlowStatementBuilder) {
         setProperty(KEY_DATABASE_SLOW_STATEMENT, databaseSlowStatementBuilder);
         return this;
     }
@@ -114,7 +115,7 @@ public class Binding extends groovy.lang.Binding {
         return (SampledTraceBuilder) getProperty(KEY_SAMPLED_TRACE);
     }
 
-    public Binding sampledTrace(SampledTraceBuilder sampledTraceBuilder) {
+    public Binding sampledTrace(final SampledTraceBuilder sampledTraceBuilder) {
         setProperty(KEY_SAMPLED_TRACE, sampledTraceBuilder);
         return this;
     }
@@ -142,41 +143,27 @@ public class Binding extends groovy.lang.Binding {
         return (boolean) getProperty(KEY_ABORT);
     }
 
-    /**
-     * Set the metrics container to store all metrics generated from the pipeline,
-     * if no container is set, all generated metrics will be sent to MAL engine for further processing,
-     * if metrics container is set, all metrics are only stored in the container, and won't be sent to MAL.
-     *
-     * @param container the metrics container
-     */
-    public Binding metricsContainer(List<SampleFamily> container) {
+    public Binding metricsContainer(final List<SampleFamily> container) {
         setProperty(KEY_METRICS_CONTAINER, container);
         return this;
     }
 
+    @SuppressWarnings("unchecked")
     public Optional<List<SampleFamily>> metricsContainer() {
-        // noinspection unchecked
         return Optional.ofNullable((List<SampleFamily>) getProperty(KEY_METRICS_CONTAINER));
     }
 
-    /**
-     * Set the log container to store the final log if it should be persisted in storage,
-     * if no container is set, the final log will be sent to source receiver,
-     * if log container is set, the log is only stored in the container, and won't be sent to source receiver.
-     *
-     * @param container the log container
-     */
-    public Binding logContainer(AtomicReference<Log> container) {
+    public Binding logContainer(final AtomicReference<Log> container) {
         setProperty(KEY_LOG_CONTAINER, container);
         return this;
     }
 
+    @SuppressWarnings("unchecked")
     public Optional<AtomicReference<Log>> logContainer() {
-        // noinspection unchecked
         return Optional.ofNullable((AtomicReference<Log>) getProperty(KEY_LOG_CONTAINER));
     }
 
-    public static class Parsed extends GroovyObjectSupport {
+    public static class Parsed {
         @Getter
         private Matcher matcher;
 
@@ -206,17 +193,20 @@ public class Binding extends groovy.lang.Binding {
             return null;
         }
 
-        @SuppressWarnings("unused")
-        public Object propertyMissing(final String name) {
-            return getAt(name);
-        }
-
-        static Object getField(Object obj, String name) {
-            try {
-                Closure<?> c = new Closure<Object>(obj, obj) {
-                };
-                return c.getProperty(name);
-            } catch (MissingPropertyException ignored) {
+        static Object getField(final Object obj, final String name) {
+            if (obj instanceof Message) {
+                final Descriptors.FieldDescriptor fd =
+                    ((Message) obj).getDescriptorForType().findFieldByName(name);
+                if (fd != null) {
+                    return ((Message) obj).getField(fd);
+                }
+            }
+            if (obj instanceof Message.Builder) {
+                final Descriptors.FieldDescriptor fd =
+                    ((Message.Builder) obj).getDescriptorForType().findFieldByName(name);
+                if (fd != null) {
+                    return ((Message.Builder) obj).getField(fd);
+                }
             }
             return null;
         }
diff --git a/oap-server/analyzer/log-analyzer/src/main/java/org/apache/skywalking/oap/log/analyzer/dsl/DSL.java b/oap-server/analyzer/log-analyzer/src/main/java/org/apache/skywalking/oap/log/analyzer/dsl/DSL.java
index c5666d8061..b3f4e4c977 100644
--- a/oap-server/analyzer/log-analyzer/src/main/java/org/apache/skywalking/oap/log/analyzer/dsl/DSL.java
+++ b/oap-server/analyzer/log-analyzer/src/main/java/org/apache/skywalking/oap/log/analyzer/dsl/DSL.java
@@ -13,97 +13,130 @@
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
  * limitations under the License.
- *
  */
 
 package org.apache.skywalking.oap.log.analyzer.dsl;
 
-import com.google.common.collect.ImmutableList;
-
-import groovy.lang.GString;
-import groovy.lang.GroovyShell;
-import groovy.transform.CompileStatic;
-import groovy.util.DelegatingScript;
-import java.lang.reflect.Array;
-import java.util.List;
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.nio.charset.StandardCharsets;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.HashMap;
 import java.util.Map;
+import java.util.concurrent.atomic.AtomicInteger;
 import lombok.AccessLevel;
 import lombok.RequiredArgsConstructor;
-import org.apache.skywalking.oap.log.analyzer.dsl.spec.LALDelegatingScript;
+import lombok.extern.slf4j.Slf4j;
 import org.apache.skywalking.oap.log.analyzer.dsl.spec.filter.FilterSpec;
 import org.apache.skywalking.oap.log.analyzer.provider.LogAnalyzerModuleConfig;
-import org.apache.skywalking.oap.meter.analyzer.dsl.registry.ProcessRegistry;
 import org.apache.skywalking.oap.server.library.module.ModuleManager;
 import org.apache.skywalking.oap.server.library.module.ModuleStartException;
-import org.codehaus.groovy.ast.stmt.DoWhileStatement;
-import org.codehaus.groovy.ast.stmt.ForStatement;
-import org.codehaus.groovy.ast.stmt.Statement;
-import org.codehaus.groovy.ast.stmt.WhileStatement;
-import org.codehaus.groovy.control.CompilerConfiguration;
-import org.codehaus.groovy.control.customizers.ASTTransformationCustomizer;
-import org.codehaus.groovy.control.customizers.ImportCustomizer;
-import org.codehaus.groovy.control.customizers.SecureASTCustomizer;
-
-import static java.util.Collections.singletonList;
-import static java.util.Collections.singletonMap;
 
+/**
+ * Same-FQCN replacement for upstream LAL DSL.
+ * Loads pre-compiled {@link LalExpression} classes from lal-expressions.txt manifest
+ * (keyed by SHA-256 hash) instead of Groovy {@code GroovyShell} runtime compilation.
+ */
+@Slf4j
 @RequiredArgsConstructor(access = AccessLevel.PRIVATE)
 public class DSL {
-    private final DelegatingScript script;
+    private static final String MANIFEST_PATH = "META-INF/lal-expressions.txt";
+    private static volatile Map<String, String> EXPRESSION_MAP;
+    private static final AtomicInteger LOADED_COUNT = new AtomicInteger();
 
+    private final LalExpression expression;
     private final FilterSpec filterSpec;
+    private Binding binding;
 
     public static DSL of(final ModuleManager moduleManager,
                          final LogAnalyzerModuleConfig config,
                          final String dsl) throws ModuleStartException {
-        final CompilerConfiguration cc = new CompilerConfiguration();
-        final ASTTransformationCustomizer customizer =
-            new ASTTransformationCustomizer(
-                singletonMap(
-                    "extensions",
-                    singletonList(LALPrecompiledExtension.class.getName())
-                ),
-                CompileStatic.class
-            );
-        cc.addCompilationCustomizers(customizer);
-        final SecureASTCustomizer secureASTCustomizer = new SecureASTCustomizer();
-        secureASTCustomizer.setDisallowedStatements(
-            ImmutableList.<Class<? extends Statement>>builder()
-                         .add(WhileStatement.class)
-                         .add(DoWhileStatement.class)
-                         .add(ForStatement.class)
-                         .build());
-        // noinspection rawtypes
-        secureASTCustomizer.setAllowedReceiversClasses(
-            ImmutableList.<Class>builder()
-                         .add(Object.class)
-                         .add(Map.class)
-                         .add(List.class)
-                         .add(Array.class)
-                         .add(GString.class)
-                         .add(String.class)
-                         .add(ProcessRegistry.class)
-                         .build());
-        cc.addCompilationCustomizers(secureASTCustomizer);
-        cc.setScriptBaseClass(LALDelegatingScript.class.getName());
-
-        ImportCustomizer icz = new ImportCustomizer();
-        icz.addImport("ProcessRegistry", ProcessRegistry.class.getName());
-        cc.addCompilationCustomizers(icz);
+        final Map<String, String> exprMap = loadManifest();
+        final String dslHash = sha256(dsl);
+        final String className = exprMap.get(dslHash);
+        if (className == null) {
+            throw new ModuleStartException(
+                "Pre-compiled LAL expression not found for DSL hash: " + dslHash
+                    + ". Available: " + exprMap.size() + " expressions.");
+        }
 
-        final GroovyShell sh = new GroovyShell(cc);
-        final DelegatingScript script = (DelegatingScript) sh.parse(dsl);
-        final FilterSpec filterSpec = new FilterSpec(moduleManager, config);
-        script.setDelegate(filterSpec);
-
-        return new DSL(script, filterSpec);
+        try {
+            final Class<?> exprClass = Class.forName(className);
+            final LalExpression expression = (LalExpression) exprClass.getDeclaredConstructor().newInstance();
+            final FilterSpec filterSpec = new FilterSpec(moduleManager, config);
+            final int count = LOADED_COUNT.incrementAndGet();
+            log.debug("Loaded pre-compiled LAL expression [{}/{}]: {}", count, exprMap.size(), className);
+            return new DSL(expression, filterSpec);
+        } catch (ClassNotFoundException e) {
+            throw new ModuleStartException(
+                "Pre-compiled LAL expression class not found: " + className, e);
+        } catch (ReflectiveOperationException e) {
+            throw new ModuleStartException(
+                "Pre-compiled LAL expression instantiation failed: " + className, e);
+        }
     }
 
     public void bind(final Binding binding) {
+        this.binding = binding;
         this.filterSpec.bind(binding);
     }
 
     public void evaluate() {
-        script.run();
+        expression.execute(filterSpec, binding);
+    }
+
+    private static Map<String, String> loadManifest() {
+        if (EXPRESSION_MAP != null) {
+            return EXPRESSION_MAP;
+        }
+        synchronized (DSL.class) {
+            if (EXPRESSION_MAP != null) {
+                return EXPRESSION_MAP;
+            }
+            final Map<String, String> map = new HashMap<>();
+            try (InputStream is = DSL.class.getClassLoader().getResourceAsStream(MANIFEST_PATH)) {
+                if (is == null) {
+                    log.warn("LAL expression manifest not found: {}", MANIFEST_PATH);
+                    EXPRESSION_MAP = map;
+                    return map;
+                }
+                try (BufferedReader reader = new BufferedReader(
+                    new InputStreamReader(is, StandardCharsets.UTF_8))) {
+                    String line;
+                    while ((line = reader.readLine()) != null) {
+                        line = line.trim();
+                        if (line.isEmpty()) {
+                            continue;
+                        }
+                        final String[] parts = line.split("=", 2);
+                        if (parts.length == 2) {
+                            map.put(parts[0], parts[1]);
+                        }
+                    }
+                }
+            } catch (IOException e) {
+                throw new IllegalStateException("Failed to load LAL expression manifest", e);
+            }
+            log.info("Loaded {} pre-compiled LAL expressions from manifest", map.size());
+            EXPRESSION_MAP = map;
+            return map;
+        }
+    }
+
+    static String sha256(final String input) {
+        try {
+            final MessageDigest digest = MessageDigest.getInstance("SHA-256");
+            final byte[] hash = digest.digest(input.getBytes(StandardCharsets.UTF_8));
+            final StringBuilder hex = new StringBuilder();
+            for (final byte b : hash) {
+                hex.append(String.format("%02x", b));
+            }
+            return hex.toString();
+        } catch (NoSuchAlgorithmException e) {
+            throw new IllegalStateException("SHA-256 not available", e);
+        }
     }
 }
diff --git a/oap-server/analyzer/log-analyzer/src/main/java/org/apache/skywalking/oap/log/analyzer/dsl/spec/AbstractSpec.java b/oap-server/analyzer/log-analyzer/src/main/java/org/apache/skywalking/oap/log/analyzer/dsl/spec/AbstractSpec.java
index d815095776..6b8e947cb8 100644
--- a/oap-server/analyzer/log-analyzer/src/main/java/org/apache/skywalking/oap/log/analyzer/dsl/spec/AbstractSpec.java
+++ b/oap-server/analyzer/log-analyzer/src/main/java/org/apache/skywalking/oap/log/analyzer/dsl/spec/AbstractSpec.java
@@ -18,7 +18,7 @@
 
 package org.apache.skywalking.oap.log.analyzer.dsl.spec;
 
-import groovy.lang.Closure;
+import java.util.function.Consumer;
 import lombok.Getter;
 import lombok.RequiredArgsConstructor;
 import lombok.experimental.Accessors;
@@ -42,15 +42,10 @@ public abstract class AbstractSpec {
     }
 
     @SuppressWarnings("unused")
-    public void abort(final Closure<Void> cl) {
+    public void abort(final Consumer<Void> cl) {
         BINDING.get().abort();
     }
 
-    @SuppressWarnings("unused")
-    public Object propertyMissing(final String name) {
-        return BINDING.get().getVariable(name);
-    }
-
     @SuppressWarnings("unused")
     public String tag(String key) {
         return BINDING.get().log().getTags().getDataList()
diff --git a/oap-server/analyzer/log-analyzer/src/main/java/org/apache/skywalking/oap/log/analyzer/dsl/spec/extractor/ExtractorSpec.java b/oap-server/analyzer/log-analyzer/src/main/java/org/apache/skywalking/oap/log/analyzer/dsl/spec/extractor/ExtractorSpec.java
index 2a51d10d4f..02c0186701 100644
--- a/oap-server/analyzer/log-analyzer/src/main/java/org/apache/skywalking/oap/log/analyzer/dsl/spec/extractor/ExtractorSpec.java
+++ b/oap-server/analyzer/log-analyzer/src/main/java/org/apache/skywalking/oap/log/analyzer/dsl/spec/extractor/ExtractorSpec.java
@@ -20,8 +20,6 @@ package org.apache.skywalking.oap.log.analyzer.dsl.spec.extractor;
 
 import com.google.common.base.Joiner;
 import com.google.common.collect.ImmutableMap;
-import groovy.lang.Closure;
-import groovy.lang.DelegatesTo;
 import java.text.ParseException;
 import java.text.SimpleDateFormat;
 import java.util.Collection;
@@ -241,99 +239,6 @@ public class ExtractorSpec extends AbstractSpec {
         }
     }
 
-    @SuppressWarnings("unused")
-    public void metrics(@DelegatesTo(SampleBuilder.class) final Closure<?> cl) {
-        if (BINDING.get().shouldAbort()) {
-            return;
-        }
-        final SampleBuilder builder = new SampleBuilder();
-        cl.setDelegate(builder);
-        cl.call();
-
-        final Sample sample = builder.build();
-        final SampleFamily sampleFamily = SampleFamilyBuilder.newBuilder(sample).build();
-
-        final Optional<List<SampleFamily>> possibleMetricsContainer = BINDING.get().metricsContainer();
-
-        if (possibleMetricsContainer.isPresent()) {
-            possibleMetricsContainer.get().add(sampleFamily);
-        } else {
-            metricConverts.forEach(it -> it.toMeter(
-                ImmutableMap.<String, SampleFamily>builder()
-                            .put(sample.getName(), sampleFamily)
-                            .build()
-            ));
-        }
-    }
-
-    @SuppressWarnings("unused")
-    public void slowSql(@DelegatesTo(SlowSqlSpec.class) final Closure<?> cl) {
-        if (BINDING.get().shouldAbort()) {
-            return;
-        }
-        LogData.Builder log = BINDING.get().log();
-        if (log.getLayer() == null
-            || log.getService() == null
-            || log.getTimestamp() < 1) {
-            LOGGER.warn("SlowSql extracts failed, maybe something is not configured.");
-            return;
-        }
-        DatabaseSlowStatementBuilder builder = new DatabaseSlowStatementBuilder(namingControl);
-        builder.setLayer(Layer.nameOf(log.getLayer()));
-
-        builder.setServiceName(log.getService());
-
-        BINDING.get().databaseSlowStatement(builder);
-
-        cl.setDelegate(slowSql);
-        cl.call();
-
-        if (builder.getId() == null
-            || builder.getLatency() < 1
-            || builder.getStatement() == null) {
-            LOGGER.warn("SlowSql extracts failed, maybe something is not configured.");
-            return;
-        }
-
-        long timeBucketForDB = TimeBucket.getTimeBucket(log.getTimestamp(), DownSampling.Second);
-        builder.setTimeBucket(timeBucketForDB);
-        builder.setTimestamp(log.getTimestamp());
-
-        builder.prepare();
-        sourceReceiver.receive(builder.toDatabaseSlowStatement());
-
-        ServiceMeta serviceMeta = new ServiceMeta();
-        serviceMeta.setName(builder.getServiceName());
-        serviceMeta.setLayer(builder.getLayer());
-        long timeBucket = TimeBucket.getTimeBucket(log.getTimestamp(), DownSampling.Minute);
-        serviceMeta.setTimeBucket(timeBucket);
-        sourceReceiver.receive(serviceMeta);
-    }
-
-    @SuppressWarnings("unused")
-    public void sampledTrace(@DelegatesTo(SampledTraceSpec.class) final Closure<?> cl) {
-        if (BINDING.get().shouldAbort()) {
-            return;
-        }
-        LogData.Builder log = BINDING.get().log();
-        SampledTraceBuilder builder = new SampledTraceBuilder(namingControl);
-        builder.setLayer(log.getLayer());
-        builder.setTimestamp(log.getTimestamp());
-        builder.setServiceName(log.getService());
-        builder.setServiceInstanceName(log.getServiceInstance());
-        builder.setTraceId(log.getTraceContext().getTraceId());
-        BINDING.get().sampledTrace(builder);
-
-        cl.setDelegate(sampledTrace);
-        cl.call();
-
-        builder.validate();
-        final Record record = builder.toRecord();
-        final ISource entity = builder.toEntity();
-        RecordStreamProcessor.getInstance().in(record);
-        sourceReceiver.receive(entity);
-    }
-
     public void metrics(final Consumer<SampleBuilder> consumer) {
         if (BINDING.get().shouldAbort()) {
             return;
diff --git a/oap-server/analyzer/log-analyzer/src/main/java/org/apache/skywalking/oap/log/analyzer/dsl/spec/filter/FilterSpec.java b/oap-server/analyzer/log-analyzer/src/main/java/org/apache/skywalking/oap/log/analyzer/dsl/spec/filter/FilterSpec.java
index b83e71b8b8..c9e3338df6 100644
--- a/oap-server/analyzer/log-analyzer/src/main/java/org/apache/skywalking/oap/log/analyzer/dsl/spec/filter/FilterSpec.java
+++ b/oap-server/analyzer/log-analyzer/src/main/java/org/apache/skywalking/oap/log/analyzer/dsl/spec/filter/FilterSpec.java
@@ -21,8 +21,6 @@ package org.apache.skywalking.oap.log.analyzer.dsl.spec.filter;
 import com.fasterxml.jackson.core.type.TypeReference;
 import com.google.protobuf.Message;
 import com.google.protobuf.TextFormat;
-import groovy.lang.Closure;
-import groovy.lang.DelegatesTo;
 import java.util.Arrays;
 import java.util.List;
 import java.util.Map;
@@ -87,110 +85,6 @@ public class FilterSpec extends AbstractSpec {
         sink = new SinkSpec(moduleManager(), moduleConfig());
     }
 
-    @SuppressWarnings("unused")
-    public void text(@DelegatesTo(TextParserSpec.class) final Closure<?> cl) {
-        if (BINDING.get().shouldAbort()) {
-            return;
-        }
-        cl.setDelegate(textParser);
-        cl.call();
-    }
-
-    @SuppressWarnings("unused")
-    public void json(@DelegatesTo(JsonParserSpec.class) final Closure<?> cl) {
-        if (BINDING.get().shouldAbort()) {
-            return;
-        }
-        cl.setDelegate(jsonParser);
-        cl.call();
-
-        final LogData.Builder logData = BINDING.get().log();
-        try {
-
-            final Map<String, Object> parsed = jsonParser.create().readValue(
-                logData.getBody().getJson().getJson(), parsedType
-            );
-
-            BINDING.get().parsed(parsed);
-        } catch (final Exception e) {
-            if (jsonParser.abortOnFailure()) {
-                BINDING.get().abort();
-            }
-        }
-    }
-
-    @SuppressWarnings({"unused"})
-    public void yaml(@DelegatesTo(YamlParserSpec.class) final Closure<?> cl) {
-        if (BINDING.get().shouldAbort()) {
-            return;
-        }
-        cl.setDelegate(yamlParser);
-        cl.call();
-
-        final LogData.Builder logData = BINDING.get().log();
-        try {
-            final Map<String, Object> parsed = yamlParser.create().load(
-                    logData.getBody().getYaml().getYaml()
-            );
-
-            BINDING.get().parsed(parsed);
-        } catch (final Exception e) {
-            if (yamlParser.abortOnFailure()) {
-                BINDING.get().abort();
-            }
-        }
-    }
-
-    @SuppressWarnings("unused")
-    public void extractor(@DelegatesTo(ExtractorSpec.class) final Closure<?> cl) {
-        if (BINDING.get().shouldAbort()) {
-            return;
-        }
-        cl.setDelegate(extractor);
-        cl.call();
-    }
-
-    @SuppressWarnings("unused")
-    public void sink(@DelegatesTo(SinkSpec.class) final Closure<?> cl) {
-        if (BINDING.get().shouldAbort()) {
-            return;
-        }
-        cl.setDelegate(sink);
-        cl.call();
-
-        final Binding b = BINDING.get();
-        final LogData.Builder logData = b.log();
-        final Message extraLog = b.extraLog();
-
-        if (!b.shouldSave()) {
-            if (LOGGER.isDebugEnabled()) {
-                LOGGER.debug("Log is dropped: {}", TextFormat.shortDebugString(logData));
-            }
-            return;
-        }
-
-        final Optional<AtomicReference<Log>> container = BINDING.get().logContainer();
-        if (container.isPresent()) {
-            sinkListenerFactories.stream()
-                     .map(LogSinkListenerFactory::create)
-                     .filter(it -> it instanceof RecordSinkListener)
-                     .map(it -> it.parse(logData, extraLog))
-                     .map(it -> (RecordSinkListener) it)
-                     .map(RecordSinkListener::getLog)
-                     .findFirst()
-                     .ifPresent(log -> container.get().set(log));
-        } else {
-            sinkListenerFactories.stream()
-                     .map(LogSinkListenerFactory::create)
-                     .forEach(it -> it.parse(logData, extraLog).build());
-        }
-    }
-
-    @SuppressWarnings("unused")
-    public void filter(final Closure<?> cl) {
-        cl.call();
-    }
-
     public void text(final Consumer<TextParserSpec> consumer) {
         if (BINDING.get().shouldAbort()) {
             return;
diff --git a/oap-server/analyzer/log-analyzer/src/main/java/org/apache/skywalking/oap/log/analyzer/dsl/spec/sink/SamplerSpec.java b/oap-server/analyzer/log-analyzer/src/main/java/org/apache/skywalking/oap/log/analyzer/dsl/spec/sink/SamplerSpec.java
index f10d471ee1..7e1ff921ad 100644
--- a/oap-server/analyzer/log-analyzer/src/main/java/org/apache/skywalking/oap/log/analyzer/dsl/spec/sink/SamplerSpec.java
+++ b/oap-server/analyzer/log-analyzer/src/main/java/org/apache/skywalking/oap/log/analyzer/dsl/spec/sink/SamplerSpec.java
@@ -18,21 +18,16 @@
 
 package org.apache.skywalking.oap.log.analyzer.dsl.spec.sink;
 
-import groovy.lang.Closure;
-import groovy.lang.DelegatesTo;
-import groovy.lang.GString;
 import java.util.Map;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.function.Consumer;
 import org.apache.skywalking.oap.log.analyzer.dsl.spec.AbstractSpec;
-import org.apache.skywalking.oap.log.analyzer.dsl.spec.sink.sampler.PossibilitySampler;
 import org.apache.skywalking.oap.log.analyzer.dsl.spec.sink.sampler.RateLimitingSampler;
 import org.apache.skywalking.oap.log.analyzer.dsl.spec.sink.sampler.Sampler;
 import org.apache.skywalking.oap.log.analyzer.provider.LogAnalyzerModuleConfig;
 import org.apache.skywalking.oap.server.library.module.ModuleManager;
 
 public class SamplerSpec extends AbstractSpec {
-    private final Map<GString, Sampler> rateLimitSamplers;
     private final Map<String, Sampler> rateLimitSamplersByString;
     private final Map<Integer, Sampler> possibilitySamplers;
     private final RateLimitingSampler.ResetHandler rlsResetHandler;
@@ -41,26 +36,11 @@ public class SamplerSpec extends AbstractSpec {
                        final LogAnalyzerModuleConfig moduleConfig) {
         super(moduleManager, moduleConfig);
 
-        rateLimitSamplers = new ConcurrentHashMap<>();
         rateLimitSamplersByString = new ConcurrentHashMap<>();
         possibilitySamplers = new ConcurrentHashMap<>();
         rlsResetHandler = new RateLimitingSampler.ResetHandler();
     }
 
-    @SuppressWarnings("unused")
-    public void rateLimit(final GString id, @DelegatesTo(RateLimitingSampler.class) final Closure<?> cl) {
-        if (BINDING.get().shouldAbort()) {
-            return;
-        }
-
-        final Sampler sampler = rateLimitSamplers.computeIfAbsent(id, $ -> new RateLimitingSampler(rlsResetHandler).start());
-
-        cl.setDelegate(sampler);
-        cl.call();
-
-        sampleWith(sampler);
-    }
-
     @SuppressWarnings("unused")
     public void rateLimit(final String id, final Consumer<RateLimitingSampler> consumer) {
         if (BINDING.get().shouldAbort()) {
@@ -75,20 +55,6 @@ public class SamplerSpec extends AbstractSpec {
         sampleWith(sampler);
     }
 
-    @SuppressWarnings("unused")
-    public void possibility(final int percentage, @DelegatesTo(PossibilitySampler.class) final Closure<?> cl) {
-        if (BINDING.get().shouldAbort()) {
-            return;
-        }
-
-        final Sampler sampler = possibilitySamplers.computeIfAbsent(percentage, $ -> new PossibilitySampler(percentage).start());
-
-        cl.setDelegate(sampler);
-        cl.call();
-
-        sampleWith(sampler);
-    }
-
     private void sampleWith(final Sampler sampler) {
         if (BINDING.get().shouldAbort()) {
             return;
diff --git a/oap-server/analyzer/log-analyzer/src/main/java/org/apache/skywalking/oap/log/analyzer/dsl/spec/sink/SinkSpec.java b/oap-server/analyzer/log-analyzer/src/main/java/org/apache/skywalking/oap/log/analyzer/dsl/spec/sink/SinkSpec.java
index f2ae371a21..631cccfca9 100644
--- a/oap-server/analyzer/log-analyzer/src/main/java/org/apache/skywalking/oap/log/analyzer/dsl/spec/sink/SinkSpec.java
+++ b/oap-server/analyzer/log-analyzer/src/main/java/org/apache/skywalking/oap/log/analyzer/dsl/spec/sink/SinkSpec.java
@@ -18,8 +18,6 @@
 
 package org.apache.skywalking.oap.log.analyzer.dsl.spec.sink;
 
-import groovy.lang.Closure;
-import groovy.lang.DelegatesTo;
 import java.util.function.Consumer;
 import org.apache.skywalking.oap.log.analyzer.dsl.spec.AbstractSpec;
 import org.apache.skywalking.oap.log.analyzer.provider.LogAnalyzerModuleConfig;
@@ -36,15 +34,6 @@ public class SinkSpec extends AbstractSpec {
         sampler = new SamplerSpec(moduleManager(), moduleConfig());
     }
 
-    @SuppressWarnings("unused")
-    public void sampler(@DelegatesTo(SamplerSpec.class) final Closure<?> cl) {
-        if (BINDING.get().shouldAbort()) {
-            return;
-        }
-        cl.setDelegate(sampler);
-        cl.call();
-    }
-
     public void sampler(final Consumer<SamplerSpec> consumer) {
         if (BINDING.get().shouldAbort()) {
             return;
@@ -52,14 +41,6 @@ public class SinkSpec extends AbstractSpec {
         consumer.accept(sampler);
     }
 
-    @SuppressWarnings("unused")
-    public void enforcer(final Closure<?> cl) {
-        if (BINDING.get().shouldAbort()) {
-            return;
-        }
-        BINDING.get().save();
-    }
-
     public void enforcer() {
         if (BINDING.get().shouldAbort()) {
             return;
@@ -67,14 +48,6 @@ public class SinkSpec extends AbstractSpec {
         BINDING.get().save();
     }
 
-    @SuppressWarnings("unused")
-    public void dropper(final Closure<?> cl) {
-        if (BINDING.get().shouldAbort()) {
-            return;
-        }
-        BINDING.get().drop();
-    }
-
     public void dropper() {
         if (BINDING.get().shouldAbort()) {
             return;
diff --git a/oap-server/analyzer/log-analyzer/src/test/java/org/apache/skywalking/oap/log/analyzer/compiler/LALClassGeneratorTest.java b/oap-server/analyzer/log-analyzer/src/test/java/org/apache/skywalking/oap/log/analyzer/compiler/LALClassGeneratorTest.java
new file mode 100644
index 0000000000..7bde461a4f
--- /dev/null
+++ b/oap-server/analyzer/log-analyzer/src/test/java/org/apache/skywalking/oap/log/analyzer/compiler/LALClassGeneratorTest.java
@@ -0,0 +1,97 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.skywalking.oap.log.analyzer.compiler;
+
+import javassist.ClassPool;
+import org.apache.skywalking.oap.log.analyzer.dsl.LalExpression;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+
+class LALClassGeneratorTest {
+
+    private LALClassGenerator generator;
+
+    @BeforeEach
+    void setUp() {
+        generator = new LALClassGenerator(new ClassPool(true));
+    }
+
+    @Test
+    void compileMinimalFilter() throws Exception {
+        final LalExpression expr = generator.compile(
+            "filter { sink {} }");
+        assertNotNull(expr);
+    }
+
+    @Test
+    void compileJsonParserFilter() throws Exception {
+        final LalExpression expr = generator.compile(
+            "filter { json {} sink {} }");
+        assertNotNull(expr);
+    }
+
+    @Test
+    void compileJsonWithExtractor() throws Exception {
+        final LalExpression expr = generator.compile(
+            "filter {\n"
+            + "  json {}\n"
+            + "  extractor {\n"
+            + "    service parsed.service as String\n"
+            + "  }\n"
+            + "  sink {}\n"
+            + "}");
+        assertNotNull(expr);
+    }
+
+    @Test
+    void compileTextWithRegexp() throws Exception {
+        final LalExpression expr = generator.compile(
+            "filter {\n"
+            + "  text {\n"
+            + "    regexp '(?<timestamp>\\\\d+) (?<level>\\\\w+) (?<msg>.*)'\n"
+            + "  }\n"
+            + "  sink {}\n"
+            + "}");
+        assertNotNull(expr);
+    }
+
+    @Test
+    void compileSinkWithEnforcer() throws Exception {
+        final LalExpression expr = generator.compile(
+            "filter {\n"
+            + "  json {}\n"
+            + "  sink {\n"
+            + "    enforcer {}\n"
+            + "  }\n"
+            + "}");
+        assertNotNull(expr);
+    }
+
+    @Test
+    void generateSourceReturnsJavaCode() {
+        final String source = generator.generateSource(
+            "filter { json {} sink {} }");
+        assertNotNull(source);
+        org.junit.jupiter.api.Assertions.assertTrue(
+            source.contains("filterSpec.json()"));
+        org.junit.jupiter.api.Assertions.assertTrue(
+            source.contains("filterSpec.sink()"));
+    }
+}
diff --git a/oap-server/analyzer/log-analyzer/src/test/java/org/apache/skywalking/oap/log/analyzer/compiler/LALScriptParserTest.java b/oap-server/analyzer/log-analyzer/src/test/java/org/apache/skywalking/oap/log/analyzer/compiler/LALScriptParserTest.java
new file mode 100644
index 0000000000..5c29397f8c
--- /dev/null
+++ b/oap-server/analyzer/log-analyzer/src/test/java/org/apache/skywalking/oap/log/analyzer/compiler/LALScriptParserTest.java
@@ -0,0 +1,192 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.skywalking.oap.log.analyzer.compiler;
+
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertInstanceOf;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+class LALScriptParserTest {
+
+    @Test
+    void parseMinimalFilter() {
+        final LALScriptModel model = LALScriptParser.parse("filter { sink {} }");
+        assertNotNull(model);
+        assertEquals(1, model.getStatements().size());
+        assertInstanceOf(LALScriptModel.SinkBlock.class, model.getStatements().get(0));
+    }
+
+    @Test
+    void parseJsonParserWithExtractorAndSink() {
+        final LALScriptModel model = LALScriptParser.parse(
+            "filter {\n"
+                + "  json {}\n"
+                + "  extractor {\n"
+                + "    service parsed.service as String\n"
+                + "    layer parsed.layer as String\n"
+                + "  }\n"
+                + "  sink {}\n"
+                + "}");
+
+        assertEquals(3, model.getStatements().size());
+        assertInstanceOf(LALScriptModel.JsonParser.class, model.getStatements().get(0));
+
+        final LALScriptModel.ExtractorBlock extractor =
+            (LALScriptModel.ExtractorBlock) model.getStatements().get(1);
+        assertEquals(2, extractor.getStatements().size());
+
+        final LALScriptModel.FieldAssignment serviceField =
+            (LALScriptModel.FieldAssignment) extractor.getStatements().get(0);
+        assertEquals(LALScriptModel.FieldType.SERVICE, serviceField.getFieldType());
+        assertTrue(serviceField.getValue().isParsedRef());
+        assertEquals("String", serviceField.getCastType());
+    }
+
+    @Test
+    void parseTextParserWithRegexp() {
+        final LALScriptModel model = LALScriptParser.parse(
+            "filter {\n"
+                + "  text {\n"
+                + "    regexp '.+\"(?<request>.+)\"(?<status>\\d{3}).+'\n"
+                + "  }\n"
+                + "  sink {}\n"
+                + "}");
+
+        assertEquals(2, model.getStatements().size());
+        final LALScriptModel.TextParser textParser =
+            (LALScriptModel.TextParser) model.getStatements().get(0);
+        assertNotNull(textParser.getRegexpPattern());
+    }
+
+    @Test
+    void parseSlowSql() {
+        final LALScriptModel model = LALScriptParser.parse(
+            "filter {\n"
+                + "  json {}\n"
+                + "  extractor {\n"
+                + "    layer parsed.layer as String\n"
+                + "    service parsed.service as String\n"
+                + "    timestamp parsed.time as String\n"
+                + "    slowSql {\n"
+                + "      id parsed.id as String\n"
+                + "      statement parsed.statement as String\n"
+                + "      latency parsed.query_time as Long\n"
+                + "    }\n"
+                + "  }\n"
+                + "}");
+
+        final LALScriptModel.ExtractorBlock extractor =
+            (LALScriptModel.ExtractorBlock) model.getStatements().get(1);
+
+        // Find the slowSql block
+        LALScriptModel.SlowSqlBlock slowSql = null;
+        for (final LALScriptModel.ExtractorStatement stmt : extractor.getStatements()) {
+            if (stmt instanceof LALScriptModel.SlowSqlBlock) {
+                slowSql = (LALScriptModel.SlowSqlBlock) stmt;
+            }
+        }
+        assertNotNull(slowSql);
+        assertNotNull(slowSql.getId());
+        assertEquals("String", slowSql.getIdCast());
+        assertNotNull(slowSql.getStatement());
+        assertEquals("String", slowSql.getStatementCast());
+        assertNotNull(slowSql.getLatency());
+        assertEquals("Long", slowSql.getLatencyCast());
+    }
+
+    @Test
+    void parseMetricsBlock() {
+        final LALScriptModel model = LALScriptParser.parse(
+            "filter {\n"
+                + "  extractor {\n"
+                + "    metrics {\n"
+                + "      timestamp log.timestamp as Long\n"
+                + "      labels level: parsed.level, service: log.service\n"
+                + "      name \"nginx_error_log_count\"\n"
+                + "      value 1\n"
+                + "    }\n"
+                + "  }\n"
+                + "  sink {}\n"
+                + "}");
+
+        final LALScriptModel.ExtractorBlock extractor =
+            (LALScriptModel.ExtractorBlock) model.getStatements().get(0);
+        final LALScriptModel.MetricsBlock metrics =
+            (LALScriptModel.MetricsBlock) extractor.getStatements().get(0);
+
+        assertEquals("nginx_error_log_count", metrics.getName());
+        assertEquals(2, metrics.getLabels().size());
+        assertTrue(metrics.getLabels().containsKey("level"));
+        assertTrue(metrics.getLabels().containsKey("service"));
+        assertNotNull(metrics.getValue());
+    }
+
+    @Test
+    void parseSinkWithSampler() {
+        final LALScriptModel model = LALScriptParser.parse(
+            "filter {\n"
+                + "  sink {\n"
+                + "    sampler {\n"
+                + "      rateLimit('service:error') {\n"
+                + "        rpm 6000\n"
+                + "      }\n"
+                + "    }\n"
+                + "  }\n"
+                + "}");
+
+        final LALScriptModel.SinkBlock sink =
+            (LALScriptModel.SinkBlock) model.getStatements().get(0);
+        assertEquals(1, sink.getStatements().size());
+        final LALScriptModel.SamplerBlock sampler =
+            (LALScriptModel.SamplerBlock) sink.getStatements().get(0);
+        assertEquals(1, sampler.getContents().size());
+        final LALScriptModel.RateLimitBlock rateLimit =
+            (LALScriptModel.RateLimitBlock) sampler.getContents().get(0);
+        assertEquals("service:error", rateLimit.getId());
+        assertEquals(6000, rateLimit.getRpm());
+    }
+
+    @Test
+    void parseIfCondition() {
+        final LALScriptModel model = LALScriptParser.parse(
+            "filter {\n"
+                + "  if (parsed.status) {\n"
+                + "    extractor {\n"
+                + "      layer parsed.layer as String\n"
+                + "    }\n"
+                + "    sink {}\n"
+                + "  }\n"
+                + "}");
+
+        assertEquals(1, model.getStatements().size());
+        final LALScriptModel.IfBlock ifBlock =
+            (LALScriptModel.IfBlock) model.getStatements().get(0);
+        assertNotNull(ifBlock.getCondition());
+        assertEquals(2, ifBlock.getThenBranch().size());
+    }
+
+    @Test
+    void parseSyntaxErrorThrows() {
+        assertThrows(IllegalArgumentException.class,
+            () -> LALScriptParser.parse("filter {"));
+    }
+}
diff --git a/oap-server/analyzer/log-analyzer-v2/src/test/java/org/apache/skywalking/oap/log/analyzer/dsl/DSLV2Test.java b/oap-server/analyzer/log-analyzer/src/test/java/org/apache/skywalking/oap/log/analyzer/dsl/DSLV2Test.java
similarity index 100%
rename from oap-server/analyzer/log-analyzer-v2/src/test/java/org/apache/skywalking/oap/log/analyzer/dsl/DSLV2Test.java
rename to oap-server/analyzer/log-analyzer/src/test/java/org/apache/skywalking/oap/log/analyzer/dsl/DSLV2Test.java
diff --git a/oap-server/analyzer/mal-transpiler/src/main/java/org/apache/skywalking/oap/server/transpiler/mal/MalToJavaTranspiler.java b/oap-server/analyzer/mal-transpiler/src/main/java/org/apache/skywalking/oap/server/transpiler/mal/MalToJavaTranspiler.java
deleted file mode 100644
index c1deeb2a04..0000000000
--- a/oap-server/analyzer/mal-transpiler/src/main/java/org/apache/skywalking/oap/server/transpiler/mal/MalToJavaTranspiler.java
+++ /dev/null
@@ -1,1099 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.skywalking.oap.server.transpiler.mal;
-
-import java.io.File;
-import java.io.IOException;
-import java.io.StringWriter;
-import java.nio.file.Files;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.LinkedHashMap;
-import java.util.LinkedHashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.stream.Collectors;
-import javax.tools.JavaCompiler;
-import javax.tools.JavaFileObject;
-import javax.tools.StandardJavaFileManager;
-import javax.tools.ToolProvider;
-import lombok.extern.slf4j.Slf4j;
-import org.codehaus.groovy.ast.ClassNode;
-import org.codehaus.groovy.ast.ModuleNode;
-import org.codehaus.groovy.ast.Parameter;
-import org.codehaus.groovy.ast.expr.ArgumentListExpression;
-import org.codehaus.groovy.ast.expr.BinaryExpression;
-import org.codehaus.groovy.ast.expr.ClosureExpression;
-import org.codehaus.groovy.ast.expr.ClassExpression;
-import org.codehaus.groovy.ast.expr.ConstantExpression;
-import org.codehaus.groovy.ast.expr.DeclarationExpression;
-import org.codehaus.groovy.ast.expr.ElvisOperatorExpression;
-import org.codehaus.groovy.ast.expr.Expression;
-import org.codehaus.groovy.ast.expr.ListExpression;
-import org.codehaus.groovy.ast.expr.MapEntryExpression;
-import org.codehaus.groovy.ast.expr.MapExpression;
-import org.codehaus.groovy.ast.expr.MethodCallExpression;
-import org.codehaus.groovy.ast.expr.PropertyExpression;
-import org.codehaus.groovy.ast.expr.TernaryExpression;
-import org.codehaus.groovy.ast.expr.TupleExpression;
-import org.codehaus.groovy.ast.expr.VariableExpression;
-import org.codehaus.groovy.syntax.Types;
-import org.codehaus.groovy.ast.expr.BooleanExpression;
-import org.codehaus.groovy.ast.expr.NotExpression;
-import org.codehaus.groovy.ast.stmt.BlockStatement;
-import org.codehaus.groovy.ast.stmt.EmptyStatement;
-import org.codehaus.groovy.ast.stmt.ExpressionStatement;
-import org.codehaus.groovy.ast.stmt.IfStatement;
-import org.codehaus.groovy.ast.stmt.ReturnStatement;
-import org.codehaus.groovy.ast.stmt.Statement;
-import org.codehaus.groovy.control.CompilationUnit;
-import org.codehaus.groovy.control.CompilerConfiguration;
-import org.codehaus.groovy.control.Phases;
-
-/**
- * Transpiles Groovy MAL expressions to Java source code at build time.
- * Parses expression strings into Groovy AST (via CompilationUnit at CONVERSION phase),
- * walks AST nodes, and produces equivalent Java classes implementing MalExpression or MalFilter.
- *
- * <p>Supported AST patterns:
- * <ul>
- *   <li>Variable references: sample family lookups, DownsamplingType constants, KNOWN_TYPES</li>
- *   <li>Method chains: .sum(), .service(), .tagEqual(), .rate(), .histogram(), etc.</li>
- *   <li>Binary arithmetic with operand-swap logic per upstream ExpandoMetaClass
- *       (N-SF: sf.minus(N).negative(), N/SF: sf.newValue(v-&gt;N/v))</li>
- *   <li>tag() closures: TagFunction lambda (assignment, remove, string concat, if/else)</li>
- *   <li>Filter closures: MalFilter class (==, !=, in, truthiness, negation, &amp;&amp;, ||)</li>
- *   <li>forEach() closures: ForEachFunction lambda (var decls, if/else-if, early return)</li>
- *   <li>instance() with PropertiesExtractor closure: Map.of() from map literals</li>
- *   <li>Elvis (?:), safe navigation (?.), ternary (? :)</li>
- *   <li>Batch compilation via javax.tools.JavaCompiler + manifest writing</li>
- * </ul>
- */
-@Slf4j
-public class MalToJavaTranspiler {
-
-    public static final String GENERATED_PACKAGE =
-        "org.apache.skywalking.oap.server.core.source.oal.rt.mal";
-
-    private static final Set<String> DOWNSAMPLING_CONSTANTS = Set.of(
-        "AVG", "SUM", "LATEST", "SUM_PER_MIN", "MAX", "MIN"
-    );
-
-    private static final Set<String> KNOWN_TYPES = Set.of(
-        "Layer", "DetectPoint", "K8sRetagType", "ProcessRegistry", "TimeUnit"
-    );
-
-    // ---- Batch state tracking ----
-
-    private final Map<String, String> expressionSources = new LinkedHashMap<>();
-
-    private final Map<String, String> filterSources = new LinkedHashMap<>();
-
-    private final Map<String, String> filterLiteralToClass = new LinkedHashMap<>();
-
-    /**
-     * Transpile a MAL expression to a Java class source implementing MalExpression.
-     *
-     * @param className  simple class name (e.g. "MalExpr_meter_jvm_heap")
-     * @param expression the Groovy expression string
-     * @return generated Java source code
-     */
-    public String transpileExpression(final String className, final String expression) {
-        final ModuleNode ast = parseToAST(expression);
-        final Statement body = extractBody(ast);
-
-        final Set<String> sampleNames = new LinkedHashSet<>();
-        collectSampleNames(body, sampleNames);
-
-        final String javaBody = visitStatement(body);
-
-        final StringBuilder sb = new StringBuilder();
-        sb.append("package ").append(GENERATED_PACKAGE).append(";\n\n");
-        sb.append("import java.util.*;\n");
-        sb.append("import org.apache.skywalking.oap.meter.analyzer.dsl.*;\n");
-        sb.append("import org.apache.skywalking.oap.meter.analyzer.dsl.SampleFamilyFunctions.*;\n");
-        sb.append("import org.apache.skywalking.oap.server.core.analysis.Layer;\n");
-        sb.append("import org.apache.skywalking.oap.server.core.source.DetectPoint;\n");
-        sb.append("import org.apache.skywalking.oap.meter.analyzer.dsl.tagOpt.K8sRetagType;\n");
-        sb.append("import org.apache.skywalking.oap.meter.analyzer.dsl.registry.ProcessRegistry;\n\n");
-
-        sb.append("public class ").append(className).append(" implements MalExpression {\n");
-        sb.append("    @Override\n");
-        sb.append("    public SampleFamily run(Map<String, SampleFamily> samples) {\n");
-
-        if (!sampleNames.isEmpty()) {
-            sb.append("        ExpressionParsingContext.get().ifPresent(ctx -> {\n");
-            for (String name : sampleNames) {
-                sb.append("            ctx.getSamples().add(\"").append(escapeJava(name)).append("\");\n");
-            }
-            sb.append("        });\n");
-        }
-
-        sb.append("        return ").append(javaBody).append(";\n");
-        sb.append("    }\n");
-        sb.append("}\n");
-
-        return sb.toString();
-    }
-
-    /**
-     * Transpile a MAL filter literal to a Java class source implementing MalFilter.
-     * Filter literals are closures like: { tags -> tags.job_name == 'vm-monitoring' }
-     *
-     * @param className     simple class name (e.g. "MalFilter_0")
-     * @param filterLiteral the Groovy closure literal string
-     * @return generated Java source code
-     */
-    public String transpileFilter(final String className, final String filterLiteral) {
-        final ModuleNode ast = parseToAST(filterLiteral);
-        final Statement body = extractBody(ast);
-
-        // The filter literal is a closure expression at the top level
-        final ClosureExpression closure = extractClosure(body);
-        final Parameter[] params = closure.getParameters();
-        final String tagsVar = (params != null && params.length > 0) ? params[0].getName() : "tags";
-
-        // Get the body expression — may need to unwrap inner block/closure
-        final Expression bodyExpr = extractFilterBodyExpr(closure.getCode(), tagsVar);
-
-        // Generate the boolean condition
-        final String condition = visitFilterCondition(bodyExpr, tagsVar);
-
-        final StringBuilder sb = new StringBuilder();
-        sb.append("package ").append(GENERATED_PACKAGE).append(";\n\n");
-        sb.append("import java.util.*;\n");
-        sb.append("import org.apache.skywalking.oap.meter.analyzer.dsl.*;\n\n");
-
-        sb.append("public class ").append(className).append(" implements MalFilter {\n");
-        sb.append("    @Override\n");
-        sb.append("    public boolean test(Map<String, String> tags) {\n");
-        sb.append("        return ").append(condition).append(";\n");
-        sb.append("    }\n");
-        sb.append("}\n");
-
-        return sb.toString();
-    }
-
-    private ClosureExpression extractClosure(final Statement body) {
-        final List<Statement> stmts = getStatements(body);
-        if (stmts.size() == 1 && stmts.get(0) instanceof ExpressionStatement) {
-            final Expression expr = ((ExpressionStatement) stmts.get(0)).getExpression();
-            if (expr instanceof ClosureExpression) {
-                return (ClosureExpression) expr;
-            }
-        }
-        throw new IllegalStateException(
-            "Filter literal must be a single closure expression, got: "
-                + (stmts.isEmpty() ? "empty" : stmts.get(0).getClass().getSimpleName()));
-    }
-
-    private Expression extractFilterBodyExpr(final Statement code, final String tagsVar) {
-        final List<Statement> stmts = getStatements(code);
-        if (stmts.isEmpty()) {
-            throw new IllegalStateException("Empty filter closure body");
-        }
-
-        final Statement last = stmts.get(stmts.size() - 1);
-        Expression expr;
-        if (last instanceof ExpressionStatement) {
-            expr = ((ExpressionStatement) last).getExpression();
-        } else if (last instanceof ReturnStatement) {
-            expr = ((ReturnStatement) last).getExpression();
-        } else if (last instanceof BlockStatement) {
-            return extractFilterBodyExpr(last, tagsVar);
-        } else {
-            throw new UnsupportedOperationException(
-                "Unsupported filter body statement: " + last.getClass().getSimpleName());
-        }
-
-        if (expr instanceof ClosureExpression) {
-            final ClosureExpression inner = (ClosureExpression) expr;
-            return extractFilterBodyExpr(inner.getCode(), tagsVar);
-        }
-
-        return expr;
-    }
-
-    // ---- AST Parsing ----
-
-    ModuleNode parseToAST(final String expression) {
-        final CompilerConfiguration cc = new CompilerConfiguration();
-        final CompilationUnit cu = new CompilationUnit(cc);
-        cu.addSource("Script", expression);
-        cu.compile(Phases.CONVERSION);
-        final List<ModuleNode> modules = cu.getAST().getModules();
-        if (modules.isEmpty()) {
-            throw new IllegalStateException("No AST modules produced for: " + expression);
-        }
-        return modules.get(0);
-    }
-
-    Statement extractBody(final ModuleNode module) {
-        final BlockStatement block = module.getStatementBlock();
-        if (block != null && !block.getStatements().isEmpty()) {
-            return block;
-        }
-        final List<ClassNode> classes = module.getClasses();
-        if (!classes.isEmpty()) {
-            return module.getStatementBlock();
-        }
-        throw new IllegalStateException("Empty AST body");
-    }
-
-    // ---- Sample Name Collection ----
-
-    private void collectSampleNames(final Statement stmt, final Set<String> names) {
-        if (stmt instanceof BlockStatement) {
-            for (Statement s : ((BlockStatement) stmt).getStatements()) {
-                collectSampleNames(s, names);
-            }
-        } else if (stmt instanceof ExpressionStatement) {
-            collectSampleNamesFromExpr(((ExpressionStatement) stmt).getExpression(), names);
-        } else if (stmt instanceof ReturnStatement) {
-            collectSampleNamesFromExpr(((ReturnStatement) stmt).getExpression(), names);
-        }
-    }
-
-    void collectSampleNamesFromExpr(final Expression expr, final Set<String> names) {
-        if (expr instanceof VariableExpression) {
-            final String name = ((VariableExpression) expr).getName();
-            if (!DOWNSAMPLING_CONSTANTS.contains(name)
-                && !KNOWN_TYPES.contains(name)
-                && !name.equals("this") && !name.equals("time")) {
-                names.add(name);
-            }
-        } else if (expr instanceof BinaryExpression) {
-            final BinaryExpression bin = (BinaryExpression) expr;
-            collectSampleNamesFromExpr(bin.getLeftExpression(), names);
-            collectSampleNamesFromExpr(bin.getRightExpression(), names);
-        } else if (expr instanceof PropertyExpression) {
-            final PropertyExpression pe = (PropertyExpression) expr;
-            collectSampleNamesFromExpr(pe.getObjectExpression(), names);
-        } else if (expr instanceof MethodCallExpression) {
-            final MethodCallExpression mce = (MethodCallExpression) expr;
-            collectSampleNamesFromExpr(mce.getObjectExpression(), names);
-            collectSampleNamesFromExpr(mce.getArguments(), names);
-        } else if (expr instanceof ArgumentListExpression) {
-            for (Expression e : ((ArgumentListExpression) expr).getExpressions()) {
-                collectSampleNamesFromExpr(e, names);
-            }
-        } else if (expr instanceof TupleExpression) {
-            for (Expression e : ((TupleExpression) expr).getExpressions()) {
-                collectSampleNamesFromExpr(e, names);
-            }
-        }
-    }
-
-    // ---- Statement Visiting ----
-
-    String visitStatement(final Statement stmt) {
-        if (stmt instanceof BlockStatement) {
-            final List<Statement> stmts = ((BlockStatement) stmt).getStatements();
-            if (stmts.size() == 1) {
-                return visitStatement(stmts.get(0));
-            }
-            // Multi-statement: last one is the return value
-            return visitStatement(stmts.get(stmts.size() - 1));
-        } else if (stmt instanceof ExpressionStatement) {
-            return visitExpression(((ExpressionStatement) stmt).getExpression());
-        } else if (stmt instanceof ReturnStatement) {
-            return visitExpression(((ReturnStatement) stmt).getExpression());
-        }
-        throw new UnsupportedOperationException(
-            "Unsupported statement: " + stmt.getClass().getSimpleName());
-    }
-
-    // ---- Expression Visiting ----
-
-    String visitExpression(final Expression expr) {
-        if (expr instanceof VariableExpression) {
-            return visitVariable((VariableExpression) expr);
-        } else if (expr instanceof ConstantExpression) {
-            return visitConstant((ConstantExpression) expr);
-        } else if (expr instanceof MethodCallExpression) {
-            return visitMethodCall((MethodCallExpression) expr);
-        } else if (expr instanceof PropertyExpression) {
-            return visitProperty((PropertyExpression) expr);
-        } else if (expr instanceof ListExpression) {
-            return visitList((ListExpression) expr);
-        } else if (expr instanceof BinaryExpression) {
-            return visitBinary((BinaryExpression) expr);
-        } else if (expr instanceof ClosureExpression) {
-            throw new UnsupportedOperationException(
-                "Bare ClosureExpression outside method call context: " + expr.getText());
-        }
-        throw new UnsupportedOperationException(
-            "Unsupported expression (not yet implemented): "
-                + expr.getClass().getSimpleName() + " = " + expr.getText());
-    }
-
-    private String visitVariable(final VariableExpression expr) {
-        final String name = expr.getName();
-        if (DOWNSAMPLING_CONSTANTS.contains(name)) {
-            return "DownsamplingType." + name;
-        }
-        if (KNOWN_TYPES.contains(name)) {
-            return name;
-        }
-        if (name.equals("this")) {
-            return "this";
-        }
-        // Sample family lookup
-        return "samples.getOrDefault(\"" + escapeJava(name) + "\", SampleFamily.EMPTY)";
-    }
-
-    private String visitConstant(final ConstantExpression expr) {
-        final Object value = expr.getValue();
-        if (value == null) {
-            return "null";
-        }
-        if (value instanceof String) {
-            return "\"" + escapeJava((String) value) + "\"";
-        }
-        if (value instanceof Integer) {
-            return value.toString();
-        }
-        if (value instanceof Long) {
-            return value + "L";
-        }
-        if (value instanceof Double) {
-            return value.toString();
-        }
-        if (value instanceof Float) {
-            return value + "f";
-        }
-        if (value instanceof Boolean) {
-            return value.toString();
-        }
-        return value.toString();
-    }
-
-    // ---- MethodCall, Property, List ----
-
-    private String visitMethodCall(final MethodCallExpression expr) {
-        final String methodName = expr.getMethodAsString();
-        final Expression objExpr = expr.getObjectExpression();
-        final ArgumentListExpression args = toArgList(expr.getArguments());
-
-        // tag(closure) -> TagFunction lambda
-        if ("tag".equals(methodName) && args.getExpressions().size() == 1
-            && args.getExpression(0) instanceof ClosureExpression) {
-            final String obj = visitExpression(objExpr);
-            final String lambda = visitTagClosure((ClosureExpression) args.getExpression(0));
-            return obj + ".tag((TagFunction) " + lambda + ")";
-        }
-
-        // forEach(list, closure) -> ForEachFunction lambda
-        if ("forEach".equals(methodName) && args.getExpressions().size() == 2
-            && args.getExpression(1) instanceof ClosureExpression) {
-            final String obj = visitExpression(objExpr);
-            final String list = visitExpression(args.getExpression(0));
-            final String lambda = visitForEachClosure((ClosureExpression) args.getExpression(1));
-            return obj + ".forEach(" + list + ", (ForEachFunction) " + lambda + ")";
-        }
-
-        // instance(..., closure) -> last arg is PropertiesExtractor lambda
-        if ("instance".equals(methodName) && !args.getExpressions().isEmpty()) {
-            final Expression lastArg = args.getExpression(args.getExpressions().size() - 1);
-            if (lastArg instanceof ClosureExpression) {
-                final String obj = visitExpression(objExpr);
-                final List<String> argStrs = new ArrayList<>();
-                for (int i = 0; i < args.getExpressions().size() - 1; i++) {
-                    argStrs.add(visitExpression(args.getExpression(i)));
-                }
-                final String lambda = visitPropertiesExtractorClosure((ClosureExpression) lastArg);
-                argStrs.add("(PropertiesExtractor) " + lambda);
-                return obj + ".instance(" + String.join(", ", argStrs) + ")";
-            }
-        }
-
-        final String obj = visitExpression(objExpr);
-
-        // Static method calls: ClassExpression.method(...)
-        if (objExpr instanceof ClassExpression) {
-            final String typeName = objExpr.getType().getNameWithoutPackage();
-            final List<String> argStrs = visitArgList(args);
-            return typeName + "." + methodName + "(" + String.join(", ", argStrs) + ")";
-        }
-
-        // Regular instance method call: obj.method(args)
-        final List<String> argStrs = visitArgList(args);
-        return obj + "." + methodName + "(" + String.join(", ", argStrs) + ")";
-    }
-
-    private String visitProperty(final PropertyExpression expr) {
-        final Expression obj = expr.getObjectExpression();
-        final String prop = expr.getPropertyAsString();
-
-        if (obj instanceof ClassExpression) {
-            return obj.getType().getNameWithoutPackage() + "." + prop;
-        }
-        if (obj instanceof VariableExpression) {
-            final String varName = ((VariableExpression) obj).getName();
-            if (KNOWN_TYPES.contains(varName)) {
-                return varName + "." + prop;
-            }
-        }
-
-        return visitExpression(obj) + "." + prop;
-    }
-
-    private String visitList(final ListExpression expr) {
-        final List<String> elements = new ArrayList<>();
-        for (Expression e : expr.getExpressions()) {
-            elements.add(visitExpression(e));
-        }
-        return "List.of(" + String.join(", ", elements) + ")";
-    }
-
-    // ---- Binary Arithmetic ----
-
-    private String visitBinary(final BinaryExpression expr) {
-        final int opType = expr.getOperation().getType();
-
-        if (isArithmetic(opType)) {
-            return visitArithmetic(expr.getLeftExpression(), expr.getRightExpression(), opType);
-        }
-
-        throw new UnsupportedOperationException(
-            "Unsupported binary operator (not yet implemented): "
-                + expr.getOperation().getText() + " in " + expr.getText());
-    }
-
-    /**
-     * Arithmetic with operand-swap logic per upstream ExpandoMetaClass:
-     * <pre>
-     *   SF + SF  -> left.plus(right)
-     *   SF - SF  -> left.minus(right)
-     *   SF * SF  -> left.multiply(right)
-     *   SF / SF  -> left.div(right)
-     *   SF op N  -> sf.op(N)
-     *   N + SF   -> sf.plus(N)          (swap)
-     *   N - SF   -> sf.minus(N).negative()
-     *   N * SF   -> sf.multiply(N)      (swap)
-     *   N / SF   -> sf.newValue(v -> N / v)
-     *   N op N   -> plain arithmetic
-     * </pre>
-     */
-    private String visitArithmetic(final Expression left, final Expression right, final int opType) {
-        final boolean leftNum = isNumberLiteral(left);
-        final boolean rightNum = isNumberLiteral(right);
-        final String leftStr = visitExpression(left);
-        final String rightStr = visitExpression(right);
-
-        if (leftNum && rightNum) {
-            return "(" + leftStr + " " + opSymbol(opType) + " " + rightStr + ")";
-        }
-
-        if (!leftNum && rightNum) {
-            return leftStr + "." + opMethod(opType) + "(" + rightStr + ")";
-        }
-
-        if (leftNum && !rightNum) {
-            switch (opType) {
-                case Types.PLUS:
-                    return rightStr + ".plus(" + leftStr + ")";
-                case Types.MINUS:
-                    return rightStr + ".minus(" + leftStr + ").negative()";
-                case Types.MULTIPLY:
-                    return rightStr + ".multiply(" + leftStr + ")";
-                case Types.DIVIDE:
-                    return rightStr + ".newValue(v -> " + leftStr + " / v)";
-                default:
-                    break;
-            }
-        }
-
-        // SF op SF
-        return leftStr + "." + opMethod(opType) + "(" + rightStr + ")";
-    }
-
-    private boolean isNumberLiteral(final Expression expr) {
-        if (expr instanceof ConstantExpression) {
-            return ((ConstantExpression) expr).getValue() instanceof Number;
-        }
-        return false;
-    }
-
-    private boolean isArithmetic(final int opType) {
-        return opType == Types.PLUS || opType == Types.MINUS
-            || opType == Types.MULTIPLY || opType == Types.DIVIDE;
-    }
-
-    private String opMethod(final int opType) {
-        switch (opType) {
-            case Types.PLUS: return "plus";
-            case Types.MINUS: return "minus";
-            case Types.MULTIPLY: return "multiply";
-            case Types.DIVIDE: return "div";
-            default: return "???";
-        }
-    }
-
-    private String opSymbol(final int opType) {
-        switch (opType) {
-            case Types.PLUS: return "+";
-            case Types.MINUS: return "-";
-            case Types.MULTIPLY: return "*";
-            case Types.DIVIDE: return "/";
-            default: return "?";
-        }
-    }
-
-    // ---- tag() Closure ----
-
-    private String visitTagClosure(final ClosureExpression closure) {
-        final Parameter[] params = closure.getParameters();
-        final String tagsVar = (params != null && params.length > 0) ? params[0].getName() : "tags";
-        final List<Statement> stmts = getStatements(closure.getCode());
-
-        final StringBuilder sb = new StringBuilder();
-        sb.append("(").append(tagsVar).append(" -> {\n");
-        for (Statement s : stmts) {
-            sb.append("            ").append(visitTagStatement(s, tagsVar)).append("\n");
-        }
-        sb.append("            return ").append(tagsVar).append(";\n");
-        sb.append("        })");
-        return sb.toString();
-    }
-
-    private String visitTagStatement(final Statement stmt, final String tagsVar) {
-        if (stmt instanceof ExpressionStatement) {
-            return visitTagExpr(((ExpressionStatement) stmt).getExpression(), tagsVar) + ";";
-        }
-        if (stmt instanceof ReturnStatement) {
-            return "return " + tagsVar + ";";
-        }
-        if (stmt instanceof IfStatement) {
-            return visitTagIf((IfStatement) stmt, tagsVar);
-        }
-        throw new UnsupportedOperationException(
-            "Unsupported tag closure statement: " + stmt.getClass().getSimpleName());
-    }
-
-    // ---- If/Else + Compound Conditions in tag() ----
-
-    private String visitTagIf(final IfStatement ifStmt, final String tagsVar) {
-        final String condition = visitTagCondition(ifStmt.getBooleanExpression().getExpression(), tagsVar);
-        final List<Statement> ifBody = getStatements(ifStmt.getIfBlock());
-        final Statement elseBlock = ifStmt.getElseBlock();
-
-        final StringBuilder sb = new StringBuilder();
-        sb.append("if (").append(condition).append(") {\n");
-        for (Statement s : ifBody) {
-            sb.append("                ").append(visitTagStatement(s, tagsVar)).append("\n");
-        }
-        sb.append("            }");
-
-        if (elseBlock != null && !(elseBlock instanceof EmptyStatement)) {
-            sb.append(" else {\n");
-            final List<Statement> elseBody = getStatements(elseBlock);
-            for (Statement s : elseBody) {
-                sb.append("                ").append(visitTagStatement(s, tagsVar)).append("\n");
-            }
-            sb.append("            }");
-        }
-
-        return sb.toString();
-    }
-
-    private String visitTagCondition(final Expression expr, final String tagsVar) {
-        if (expr instanceof BinaryExpression) {
-            final BinaryExpression bin = (BinaryExpression) expr;
-            final int opType = bin.getOperation().getType();
-
-            if (opType == Types.COMPARE_EQUAL) {
-                return visitTagEquals(bin.getLeftExpression(), bin.getRightExpression(), tagsVar, false);
-            }
-            if (opType == Types.COMPARE_NOT_EQUAL) {
-                return visitTagEquals(bin.getLeftExpression(), bin.getRightExpression(), tagsVar, true);
-            }
-            if (opType == Types.LOGICAL_OR) {
-                return visitTagCondition(bin.getLeftExpression(), tagsVar)
-                    + " || " + visitTagCondition(bin.getRightExpression(), tagsVar);
-            }
-            if (opType == Types.LOGICAL_AND) {
-                return visitTagCondition(bin.getLeftExpression(), tagsVar)
-                    + " && " + visitTagCondition(bin.getRightExpression(), tagsVar);
-            }
-        }
-        if (expr instanceof BooleanExpression) {
-            return visitTagCondition(((BooleanExpression) expr).getExpression(), tagsVar);
-        }
-        return visitTagValue(expr, tagsVar);
-    }
-
-    private String visitTagEquals(final Expression left, final Expression right,
-                                  final String tagsVar, final boolean negate) {
-        if (isNullConstant(right)) {
-            final String leftStr = visitTagValue(left, tagsVar);
-            return negate ? leftStr + " != null" : leftStr + " == null";
-        }
-        if (isNullConstant(left)) {
-            final String rightStr = visitTagValue(right, tagsVar);
-            return negate ? rightStr + " != null" : rightStr + " == null";
-        }
-
-        final String leftStr = visitTagValue(left, tagsVar);
-        final String rightStr = visitTagValue(right, tagsVar);
-
-        if (right instanceof ConstantExpression && ((ConstantExpression) right).getValue() instanceof String) {
-            final String result = rightStr + ".equals(" + leftStr + ")";
-            return negate ? "!" + result : result;
-        }
-        if (left instanceof ConstantExpression && ((ConstantExpression) left).getValue() instanceof String) {
-            final String result = leftStr + ".equals(" + rightStr + ")";
-            return negate ? "!" + result : result;
-        }
-        final String result = "Objects.equals(" + leftStr + ", " + rightStr + ")";
-        return negate ? "!" + result : result;
-    }
-
-    // ---- Filter Conditions ----
-
-    private String visitFilterCondition(final Expression expr, final String tagsVar) {
-        if (expr instanceof NotExpression) {
-            final Expression inner = ((NotExpression) expr).getExpression();
-            final String val = visitTagValue(inner, tagsVar);
-            return "(" + val + " == null || " + val + ".isEmpty())";
-        }
-
-        if (expr instanceof BinaryExpression) {
-            final BinaryExpression bin = (BinaryExpression) expr;
-            final int opType = bin.getOperation().getType();
-
-            if (opType == Types.COMPARE_EQUAL) {
-                return visitTagEquals(bin.getLeftExpression(), bin.getRightExpression(), tagsVar, false);
-            }
-            if (opType == Types.COMPARE_NOT_EQUAL) {
-                return visitTagEquals(bin.getLeftExpression(), bin.getRightExpression(), tagsVar, true);
-            }
-            if (opType == Types.LOGICAL_OR) {
-                return visitFilterCondition(bin.getLeftExpression(), tagsVar)
-                    + " || " + visitFilterCondition(bin.getRightExpression(), tagsVar);
-            }
-            if (opType == Types.LOGICAL_AND) {
-                return visitFilterCondition(bin.getLeftExpression(), tagsVar)
-                    + " && " + visitFilterCondition(bin.getRightExpression(), tagsVar);
-            }
-            if (opType == Types.KEYWORD_IN) {
-                final String val = visitTagValue(bin.getLeftExpression(), tagsVar);
-                final String list = visitTagValue(bin.getRightExpression(), tagsVar);
-                return list + ".contains(" + val + ")";
-            }
-        }
-
-        if (expr instanceof BooleanExpression) {
-            return visitFilterCondition(((BooleanExpression) expr).getExpression(), tagsVar);
-        }
-
-        final String val = visitTagValue(expr, tagsVar);
-        return "(" + val + " != null && !" + val + ".isEmpty())";
-    }
-
-    private String visitTagExpr(final Expression expr, final String tagsVar) {
-        if (expr instanceof BinaryExpression) {
-            final BinaryExpression bin = (BinaryExpression) expr;
-            if (bin.getOperation().getType() == Types.ASSIGN) {
-                return visitTagAssignment(bin.getLeftExpression(), bin.getRightExpression(), tagsVar);
-            }
-        }
-        if (expr instanceof MethodCallExpression) {
-            final MethodCallExpression mce = (MethodCallExpression) expr;
-            if ("remove".equals(mce.getMethodAsString()) && isTagsVar(mce.getObjectExpression(), tagsVar)) {
-                final ArgumentListExpression args = toArgList(mce.getArguments());
-                return tagsVar + ".remove(" + visitTagValue(args.getExpression(0), tagsVar) + ")";
-            }
-        }
-        return visitTagValue(expr, tagsVar);
-    }
-
-    private String visitTagAssignment(final Expression left, final Expression right, final String tagsVar) {
-        final String val = visitTagValue(right, tagsVar);
-
-        if (left instanceof PropertyExpression) {
-            final PropertyExpression prop = (PropertyExpression) left;
-            if (isTagsVar(prop.getObjectExpression(), tagsVar)) {
-                return tagsVar + ".put(\"" + escapeJava(prop.getPropertyAsString()) + "\", " + val + ")";
-            }
-        }
-        if (left instanceof BinaryExpression) {
-            final BinaryExpression sub = (BinaryExpression) left;
-            if (sub.getOperation().getType() == Types.LEFT_SQUARE_BRACKET
-                && isTagsVar(sub.getLeftExpression(), tagsVar)) {
-                final String key = visitTagValue(sub.getRightExpression(), tagsVar);
-                return tagsVar + ".put(" + key + ", " + val + ")";
-            }
-        }
-        throw new UnsupportedOperationException(
-            "Unsupported tag assignment target: " + left.getClass().getSimpleName() + " = " + left.getText());
-    }
-
-    String visitTagValue(final Expression expr, final String tagsVar) {
-        if (expr instanceof PropertyExpression) {
-            final PropertyExpression prop = (PropertyExpression) expr;
-            if (isTagsVar(prop.getObjectExpression(), tagsVar)) {
-                return tagsVar + ".get(\"" + escapeJava(prop.getPropertyAsString()) + "\")";
-            }
-            return visitProperty(prop);
-        }
-        if (expr instanceof BinaryExpression) {
-            final BinaryExpression bin = (BinaryExpression) expr;
-            if (bin.getOperation().getType() == Types.LEFT_SQUARE_BRACKET
-                && isTagsVar(bin.getLeftExpression(), tagsVar)) {
-                return tagsVar + ".get(" + visitTagValue(bin.getRightExpression(), tagsVar) + ")";
-            }
-            if (bin.getOperation().getType() == Types.PLUS) {
-                return visitTagValue(bin.getLeftExpression(), tagsVar)
-                    + " + " + visitTagValue(bin.getRightExpression(), tagsVar);
-            }
-        }
-        // Elvis operator — must check BEFORE TernaryExpression since it extends it
-        if (expr instanceof ElvisOperatorExpression) {
-            final ElvisOperatorExpression elvis = (ElvisOperatorExpression) expr;
-            final String val = visitTagValue(elvis.getTrueExpression(), tagsVar);
-            final String defaultVal = visitTagValue(elvis.getFalseExpression(), tagsVar);
-            return "(" + val + " != null ? " + val + " : " + defaultVal + ")";
-        }
-        if (expr instanceof TernaryExpression) {
-            final TernaryExpression tern = (TernaryExpression) expr;
-            final String cond = visitFilterCondition(tern.getBooleanExpression().getExpression(), tagsVar);
-            final String trueVal = visitTagValue(tern.getTrueExpression(), tagsVar);
-            final String falseVal = visitTagValue(tern.getFalseExpression(), tagsVar);
-            return "(" + cond + " ? " + trueVal + " : " + falseVal + ")";
-        }
-        if (expr instanceof MethodCallExpression) {
-            final MethodCallExpression mce = (MethodCallExpression) expr;
-            final String obj = visitTagValue(mce.getObjectExpression(), tagsVar);
-            final ArgumentListExpression args = toArgList(mce.getArguments());
-            final List<String> argStrs = new ArrayList<>();
-            for (Expression a : args.getExpressions()) {
-                argStrs.add(visitTagValue(a, tagsVar));
-            }
-            final String call = obj + "." + mce.getMethodAsString() + "(" + String.join(", ", argStrs) + ")";
-            if (mce.isSafe()) {
-                return "(" + obj + " != null ? " + call + " : null)";
-            }
-            return call;
-        }
-        if (expr instanceof VariableExpression) {
-            final String name = ((VariableExpression) expr).getName();
-            if (name.equals(tagsVar)) {
-                return tagsVar;
-            }
-            return name;
-        }
-        if (expr instanceof ConstantExpression) {
-            return visitConstant((ConstantExpression) expr);
-        }
-        if (expr instanceof ListExpression) {
-            return visitList((ListExpression) expr);
-        }
-        if (expr instanceof MapExpression) {
-            final MapExpression map = (MapExpression) expr;
-            final List<String> entries = new ArrayList<>();
-            for (MapEntryExpression entry : map.getMapEntryExpressions()) {
-                entries.add(visitTagValue(entry.getKeyExpression(), tagsVar));
-                entries.add(visitTagValue(entry.getValueExpression(), tagsVar));
-            }
-            return "Map.of(" + String.join(", ", entries) + ")";
-        }
-        return visitExpression(expr);
-    }
-
-    private boolean isTagsVar(final Expression expr, final String tagsVar) {
-        return expr instanceof VariableExpression
-            && ((VariableExpression) expr).getName().equals(tagsVar);
-    }
-
-    private List<Statement> getStatements(final Statement stmt) {
-        if (stmt instanceof BlockStatement) {
-            return ((BlockStatement) stmt).getStatements();
-        }
-        return List.of(stmt);
-    }
-
-    // ---- forEach() Closure ----
-
-    private String visitForEachClosure(final ClosureExpression closure) {
-        final Parameter[] params = closure.getParameters();
-        final String prefixVar = (params != null && params.length > 0) ? params[0].getName() : "prefix";
-        final String tagsVar = (params != null && params.length > 1) ? params[1].getName() : "tags";
-
-        final List<Statement> stmts = getStatements(closure.getCode());
-
-        final StringBuilder sb = new StringBuilder();
-        sb.append("(").append(prefixVar).append(", ").append(tagsVar).append(") -> {\n");
-        for (Statement s : stmts) {
-            sb.append("            ").append(visitForEachStatement(s, tagsVar)).append("\n");
-        }
-        sb.append("        }");
-        return sb.toString();
-    }
-
-    private String visitForEachStatement(final Statement stmt, final String tagsVar) {
-        if (stmt instanceof ExpressionStatement) {
-            return visitForEachExpr(((ExpressionStatement) stmt).getExpression(), tagsVar) + ";";
-        }
-        if (stmt instanceof ReturnStatement) {
-            return "return;";
-        }
-        if (stmt instanceof IfStatement) {
-            return visitForEachIf((IfStatement) stmt, tagsVar);
-        }
-        throw new UnsupportedOperationException(
-            "Unsupported forEach closure statement: " + stmt.getClass().getSimpleName());
-    }
-
-    private String visitForEachExpr(final Expression expr, final String tagsVar) {
-        if (expr instanceof DeclarationExpression) {
-            final DeclarationExpression decl = (DeclarationExpression) expr;
-            final String typeName = decl.getVariableExpression().getType().getNameWithoutPackage();
-            final String varName = decl.getVariableExpression().getName();
-            final String init = visitTagValue(decl.getRightExpression(), tagsVar);
-            return typeName + " " + varName + " = " + init;
-        }
-        if (expr instanceof BinaryExpression) {
-            final BinaryExpression bin = (BinaryExpression) expr;
-            if (bin.getOperation().getType() == Types.ASSIGN) {
-                final Expression left = bin.getLeftExpression();
-                if (isTagWrite(left, tagsVar)) {
-                    return visitTagAssignment(left, bin.getRightExpression(), tagsVar);
-                }
-                if (left instanceof VariableExpression) {
-                    return ((VariableExpression) left).getName()
-                        + " = " + visitTagValue(bin.getRightExpression(), tagsVar);
-                }
-            }
-        }
-        return visitTagExpr(expr, tagsVar);
-    }
-
-    private String visitForEachIf(final IfStatement ifStmt, final String tagsVar) {
-        final String condition = visitTagCondition(ifStmt.getBooleanExpression().getExpression(), tagsVar);
-        final List<Statement> ifBody = getStatements(ifStmt.getIfBlock());
-        final Statement elseBlock = ifStmt.getElseBlock();
-
-        final StringBuilder sb = new StringBuilder();
-        sb.append("if (").append(condition).append(") {\n");
-        for (Statement s : ifBody) {
-            sb.append("                ").append(visitForEachStatement(s, tagsVar)).append("\n");
-        }
-        sb.append("            }");
-
-        if (elseBlock instanceof IfStatement) {
-            sb.append(" else ").append(visitForEachIf((IfStatement) elseBlock, tagsVar));
-        } else if (elseBlock != null && !(elseBlock instanceof EmptyStatement)) {
-            sb.append(" else {\n");
-            for (Statement s : getStatements(elseBlock)) {
-                sb.append("                ").append(visitForEachStatement(s, tagsVar)).append("\n");
-            }
-            sb.append("            }");
-        }
-
-        return sb.toString();
-    }
-
-    private boolean isTagWrite(final Expression left, final String tagsVar) {
-        if (left instanceof PropertyExpression) {
-            return isTagsVar(((PropertyExpression) left).getObjectExpression(), tagsVar);
-        }
-        if (left instanceof BinaryExpression) {
-            final BinaryExpression sub = (BinaryExpression) left;
-            return sub.getOperation().getType() == Types.LEFT_SQUARE_BRACKET
-                && isTagsVar(sub.getLeftExpression(), tagsVar);
-        }
-        return false;
-    }
-
-    private boolean isNullConstant(final Expression expr) {
-        return expr instanceof ConstantExpression && ((ConstantExpression) expr).getValue() == null;
-    }
-
-    // ---- PropertiesExtractor Closure ----
-
-    private String visitPropertiesExtractorClosure(final ClosureExpression closure) {
-        final Parameter[] params = closure.getParameters();
-        final String tagsVar = (params != null && params.length > 0) ? params[0].getName() : "tags";
-        final List<Statement> stmts = getStatements(closure.getCode());
-
-        final Statement last = stmts.get(stmts.size() - 1);
-        Expression bodyExpr;
-        if (last instanceof ExpressionStatement) {
-            bodyExpr = ((ExpressionStatement) last).getExpression();
-        } else if (last instanceof ReturnStatement) {
-            bodyExpr = ((ReturnStatement) last).getExpression();
-        } else {
-            throw new UnsupportedOperationException(
-                "Unsupported PropertiesExtractor closure body: " + last.getClass().getSimpleName());
-        }
-        return "(" + tagsVar + " -> " + visitTagValue(bodyExpr, tagsVar) + ")";
-    }
-
-    // ---- Batch Registration, Compilation, and Manifest Writing ----
-
-    public void registerExpression(final String className, final String source) {
-        expressionSources.put(className, source);
-    }
-
-    public void registerFilter(final String className, final String filterLiteral, final String source) {
-        filterSources.put(className, source);
-        filterLiteralToClass.put(filterLiteral, GENERATED_PACKAGE + "." + className);
-    }
-
-    /**
-     * Compile all registered sources using javax.tools.JavaCompiler.
-     *
-     * @param sourceDir  directory to write .java source files (package dirs created automatically)
-     * @param outputDir  directory for compiled .class files
-     * @param classpath  classpath for javac (semicolon/colon-separated JAR paths)
-     * @throws IOException if file I/O fails
-     */
-    public void compileAll(final File sourceDir, final File outputDir,
-                           final String classpath) throws IOException {
-        final Map<String, String> allSources = new LinkedHashMap<>();
-        allSources.putAll(expressionSources);
-        allSources.putAll(filterSources);
-
-        if (allSources.isEmpty()) {
-            log.info("No MAL sources to compile.");
-            return;
-        }
-
-        final String packageDir = GENERATED_PACKAGE.replace('.', File.separatorChar);
-        final File srcPkgDir = new File(sourceDir, packageDir);
-        if (!srcPkgDir.exists() && !srcPkgDir.mkdirs()) {
-            throw new IOException("Failed to create source dir: " + srcPkgDir);
-        }
-        if (!outputDir.exists() && !outputDir.mkdirs()) {
-            throw new IOException("Failed to create output dir: " + outputDir);
-        }
-
-        final List<File> javaFiles = new ArrayList<>();
-        for (Map.Entry<String, String> entry : allSources.entrySet()) {
-            final File javaFile = new File(srcPkgDir, entry.getKey() + ".java");
-            Files.writeString(javaFile.toPath(), entry.getValue());
-            javaFiles.add(javaFile);
-        }
-
-        final JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
-        if (compiler == null) {
-            throw new IllegalStateException("No Java compiler available — requires JDK");
-        }
-
-        final StringWriter errorWriter = new StringWriter();
-
-        try (StandardJavaFileManager fileManager = compiler.getStandardFileManager(null, null, null)) {
-            final Iterable<? extends JavaFileObject> compilationUnits =
-                fileManager.getJavaFileObjectsFromFiles(javaFiles);
-
-            final List<String> options = Arrays.asList(
-                "-d", outputDir.getAbsolutePath(),
-                "-classpath", classpath
-            );
-
-            final JavaCompiler.CompilationTask task = compiler.getTask(
-                errorWriter, fileManager, null, options, null, compilationUnits);
-
-            final boolean success = task.call();
-            if (!success) {
-                throw new RuntimeException(
-                    "Java compilation failed for " + javaFiles.size() + " MAL sources:\n"
-                        + errorWriter);
-            }
-        }
-
-        log.info("Compiled {} MAL sources to {}", allSources.size(), outputDir);
-    }
-
-    /**
-     * Write mal-expressions.txt manifest: one FQCN per line.
-     */
-    public void writeExpressionManifest(final File outputDir) throws IOException {
-        final File manifestDir = new File(outputDir, "META-INF");
-        if (!manifestDir.exists() && !manifestDir.mkdirs()) {
-            throw new IOException("Failed to create META-INF dir: " + manifestDir);
-        }
-
-        final List<String> lines = expressionSources.keySet().stream()
-            .map(name -> GENERATED_PACKAGE + "." + name)
-            .collect(Collectors.toList());
-        Files.write(new File(manifestDir, "mal-expressions.txt").toPath(), lines);
-        log.info("Wrote mal-expressions.txt with {} entries", lines.size());
-    }
-
-    /**
-     * Write mal-filter-expressions.properties manifest: literal=FQCN.
-     */
-    public void writeFilterManifest(final File outputDir) throws IOException {
-        final File manifestDir = new File(outputDir, "META-INF");
-        if (!manifestDir.exists() && !manifestDir.mkdirs()) {
-            throw new IOException("Failed to create META-INF dir: " + manifestDir);
-        }
-
-        final List<String> lines = filterLiteralToClass.entrySet().stream()
-            .map(e -> escapeProperties(e.getKey()) + "=" + e.getValue())
-            .collect(Collectors.toList());
-        Files.write(new File(manifestDir, "mal-filter-expressions.properties").toPath(), lines);
-        log.info("Wrote mal-filter-expressions.properties with {} entries", lines.size());
-    }
-
-    private static String escapeProperties(final String s) {
-        return s.replace("\\", "\\\\")
-                .replace("=", "\\=")
-                .replace(":", "\\:")
-                .replace(" ", "\\ ");
-    }
-
-    // ---- Argument Utilities ----
-
-    private ArgumentListExpression toArgList(final Expression args) {
-        if (args instanceof ArgumentListExpression) {
-            return (ArgumentListExpression) args;
-        }
-        if (args instanceof TupleExpression) {
-            final ArgumentListExpression ale = new ArgumentListExpression();
-            for (Expression e : ((TupleExpression) args).getExpressions()) {
-                ale.addExpression(e);
-            }
-            return ale;
-        }
-        final ArgumentListExpression ale = new ArgumentListExpression();
-        ale.addExpression(args);
-        return ale;
-    }
-
-    private List<String> visitArgList(final ArgumentListExpression args) {
-        final List<String> result = new ArrayList<>();
-        for (Expression arg : args.getExpressions()) {
-            result.add(visitExpression(arg));
-        }
-        return result;
-    }
-
-    // ---- Utility ----
-
-    public static String escapeJava(final String s) {
-        return s.replace("\\", "\\\\")
-                .replace("\"", "\\\"")
-                .replace("\n", "\\n")
-                .replace("\r", "\\r")
-                .replace("\t", "\\t");
-    }
-}
diff --git a/oap-server/analyzer/mal-transpiler/src/test/java/org/apache/skywalking/oap/server/transpiler/mal/MalToJavaTranspilerTest.java b/oap-server/analyzer/mal-transpiler/src/test/java/org/apache/skywalking/oap/server/transpiler/mal/MalToJavaTranspilerTest.java
deleted file mode 100644
index 0207d8df32..0000000000
--- a/oap-server/analyzer/mal-transpiler/src/test/java/org/apache/skywalking/oap/server/transpiler/mal/MalToJavaTranspilerTest.java
+++ /dev/null
@@ -1,904 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.skywalking.oap.server.transpiler.mal;
-
-import java.io.File;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.util.List;
-
-import org.junit.jupiter.api.BeforeEach;
-import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.io.TempDir;
-
-import static org.junit.jupiter.api.Assertions.assertNotNull;
-import static org.junit.jupiter.api.Assertions.assertTrue;
-
-class MalToJavaTranspilerTest {
-
-    private MalToJavaTranspiler transpiler;
-
-    @BeforeEach
-    void setUp() {
-        transpiler = new MalToJavaTranspiler();
-    }
-
-    // ---- AST Parsing + Simple Variable References ----
-
-    @Test
-    void simpleVariableReference() {
-        final String java = transpiler.transpileExpression("MalExpr_test", "metric_name");
-        assertNotNull(java);
-
-        assertTrue(java.contains("package " + MalToJavaTranspiler.GENERATED_PACKAGE),
-            "Should have correct package");
-        assertTrue(java.contains("public class MalExpr_test implements MalExpression"),
-            "Should implement MalExpression");
-        assertTrue(java.contains("public SampleFamily run(Map<String, SampleFamily> samples)"),
-            "Should have run method");
-
-        assertTrue(java.contains("ctx.getSamples().add(\"metric_name\")"),
-            "Should track sample name in parsing context");
-
-        assertTrue(java.contains("samples.getOrDefault(\"metric_name\", SampleFamily.EMPTY)"),
-            "Should look up sample family from map");
-    }
-
-    @Test
-    void downsamplingConstantNotTrackedAsSample() {
-        final String java = transpiler.transpileExpression("MalExpr_test", "SUM");
-        assertNotNull(java);
-
-        assertTrue(!java.contains("ctx.getSamples().add(\"SUM\")"),
-            "Should not track DownsamplingType constant as sample");
-
-        assertTrue(java.contains("DownsamplingType.SUM"),
-            "Should resolve to DownsamplingType.SUM");
-    }
-
-    @Test
-    void parseToAST_producesModuleNode() {
-        final var ast = transpiler.parseToAST("some_metric");
-        assertNotNull(ast, "Should produce a ModuleNode");
-        assertNotNull(ast.getStatementBlock(), "Should have a statement block");
-    }
-
-    @Test
-    void constantString() {
-        final String java = transpiler.transpileExpression("MalExpr_test", "'hello'");
-        assertNotNull(java);
-        assertTrue(java.contains("\"hello\""),
-            "Should convert Groovy string to Java string");
-    }
-
-    @Test
-    void constantNumber() {
-        final String java = transpiler.transpileExpression("MalExpr_test", "42");
-        assertNotNull(java);
-        assertTrue(java.contains("42"),
-            "Should preserve number literal");
-    }
-
-    // ---- Method Chains + List Literals + Enum Properties ----
-
-    @Test
-    void simpleMethodChain() {
-        final String java = transpiler.transpileExpression("MalExpr_test",
-            "metric_name.sum(['a', 'b']).service(['svc'], Layer.GENERAL)");
-        assertNotNull(java);
-
-        assertTrue(java.contains(".sum(List.of(\"a\", \"b\"))"),
-            "Should translate ['a','b'] to List.of(\"a\", \"b\")");
-        assertTrue(java.contains(".service(List.of(\"svc\"), Layer.GENERAL)"),
-            "Should translate Layer.GENERAL as enum");
-    }
-
-    @Test
-    void tagEqualChain() {
-        final String java = transpiler.transpileExpression("MalExpr_test",
-            "cpu_seconds.tagNotEqual('mode', 'idle').sum(['host']).rate('PT1M')");
-        assertNotNull(java);
-
-        assertTrue(java.contains(".tagNotEqual(\"mode\", \"idle\")"),
-            "Should translate tagNotEqual with string args");
-        assertTrue(java.contains(".sum(List.of(\"host\"))"),
-            "Should translate single-element list");
-        assertTrue(java.contains(".rate(\"PT1M\")"),
-            "Should translate rate with string arg");
-    }
-
-    @Test
-    void downsamplingMethod() {
-        final String java = transpiler.transpileExpression("MalExpr_test",
-            "metric.downsampling(SUM)");
-        assertNotNull(java);
-
-        assertTrue(java.contains(".downsampling(DownsamplingType.SUM)"),
-            "Should resolve SUM to DownsamplingType.SUM");
-    }
-
-    @Test
-    void retagByK8sMeta() {
-        final String java = transpiler.transpileExpression("MalExpr_test",
-            "metric.retagByK8sMeta('service', K8sRetagType.Pod2Service, 'pod', 'namespace')");
-        assertNotNull(java);
-
-        assertTrue(java.contains(".retagByK8sMeta(\"service\", K8sRetagType.Pod2Service, \"pod\", \"namespace\")"),
-            "Should translate K8sRetagType enum and string args");
-    }
-
-    @Test
-    void histogramPercentile() {
-        final String java = transpiler.transpileExpression("MalExpr_test",
-            "metric.sum(['le', 'svc']).histogram().histogram_percentile([50, 75, 90])");
-        assertNotNull(java);
-
-        assertTrue(java.contains(".histogram()"),
-            "Should translate no-arg histogram()");
-        assertTrue(java.contains(".histogram_percentile(List.of(50, 75, 90))"),
-            "Should translate integer list");
-    }
-
-    @Test
-    void sampleNameCollectionThroughChain() {
-        final String java = transpiler.transpileExpression("MalExpr_test",
-            "my_metric.sum(['a']).service(['svc'], Layer.GENERAL)");
-        assertNotNull(java);
-
-        assertTrue(java.contains("ctx.getSamples().add(\"my_metric\")"),
-            "Should collect sample name from root of method chain");
-        assertTrue(!java.contains("ctx.getSamples().add(\"a\")"),
-            "Should NOT collect 'a' (it's a string constant arg, not a sample)");
-    }
-
-    @Test
-    void detectPointEnum() {
-        final String java = transpiler.transpileExpression("MalExpr_test",
-            "metric.serviceRelation(DetectPoint.CLIENT, ['src'], ['dst'], Layer.MESH_DP)");
-        assertNotNull(java);
-
-        assertTrue(java.contains("DetectPoint.CLIENT"),
-            "Should translate DetectPoint enum");
-        assertTrue(java.contains("Layer.MESH_DP"),
-            "Should translate Layer enum");
-    }
-
-    @Test
-    void enumImportsPresent() {
-        final String java = transpiler.transpileExpression("MalExpr_test",
-            "metric.service(['svc'], Layer.GENERAL)");
-        assertNotNull(java);
-
-        assertTrue(java.contains("import org.apache.skywalking.oap.server.core.analysis.Layer;"),
-            "Should import Layer");
-        assertTrue(java.contains("import org.apache.skywalking.oap.server.core.source.DetectPoint;"),
-            "Should import DetectPoint");
-        assertTrue(java.contains("import org.apache.skywalking.oap.meter.analyzer.dsl.tagOpt.K8sRetagType;"),
-            "Should import K8sRetagType");
-    }
-
-    // ---- Binary Arithmetic with Operand-Swap ----
-
-    @Test
-    void sfTimesNumber() {
-        final String java = transpiler.transpileExpression("MalExpr_test", "metric * 100");
-        assertNotNull(java);
-        assertTrue(java.contains(".multiply(100)"),
-            "SF * N should call .multiply(N)");
-    }
-
-    @Test
-    void sfDivNumber() {
-        final String java = transpiler.transpileExpression("MalExpr_test", "metric / 1024");
-        assertNotNull(java);
-        assertTrue(java.contains(".div(1024)"),
-            "SF / N should call .div(N)");
-    }
-
-    @Test
-    void numberMinusSf() {
-        final String java = transpiler.transpileExpression("MalExpr_test", "100 - metric");
-        assertNotNull(java);
-        assertTrue(java.contains(".minus(100).negative()"),
-            "N - SF should produce sf.minus(N).negative()");
-    }
-
-    @Test
-    void numberDivSf() {
-        final String java = transpiler.transpileExpression("MalExpr_test", "1 / metric");
-        assertNotNull(java);
-        assertTrue(java.contains(".newValue(v -> 1 / v)"),
-            "N / SF should produce sf.newValue(v -> N / v)");
-    }
-
-    @Test
-    void numberPlusSf() {
-        final String java = transpiler.transpileExpression("MalExpr_test", "10 + metric");
-        assertNotNull(java);
-        assertTrue(java.contains(".plus(10)"),
-            "N + SF should swap to sf.plus(N)");
-    }
-
-    @Test
-    void numberTimesSf() {
-        final String java = transpiler.transpileExpression("MalExpr_test", "100 * metric");
-        assertNotNull(java);
-        assertTrue(java.contains(".multiply(100)"),
-            "N * SF should swap to sf.multiply(N)");
-    }
-
-    @Test
-    void sfMinusSf() {
-        final String java = transpiler.transpileExpression("MalExpr_test", "mem_total - mem_avail");
-        assertNotNull(java);
-        assertTrue(java.contains("ctx.getSamples().add(\"mem_total\")"),
-            "Should collect both sample names");
-        assertTrue(java.contains("ctx.getSamples().add(\"mem_avail\")"),
-            "Should collect both sample names");
-        assertTrue(java.contains(".minus("),
-            "SF - SF should call .minus()");
-    }
-
-    @Test
-    void sfDivSfTimesNumber() {
-        final String java = transpiler.transpileExpression("MalExpr_test",
-            "used_bytes / max_bytes * 100");
-        assertNotNull(java);
-        assertTrue(java.contains(".div("),
-            "Should have .div() for SF / SF");
-        assertTrue(java.contains(".multiply(100)"),
-            "Should have .multiply(100) for result * 100");
-    }
-
-    @Test
-    void nestedParenArithmetic() {
-        final String java = transpiler.transpileExpression("MalExpr_test",
-            "100 - ((mem_free * 100) / mem_total)");
-        assertNotNull(java);
-        assertTrue(java.contains(".multiply(100)"),
-            "Should have inner multiply");
-        assertTrue(java.contains(".negative()"),
-            "100 - SF should produce .negative()");
-    }
-
-    @Test
-    void parenthesizedWithMethodChain() {
-        final String java = transpiler.transpileExpression("MalExpr_test",
-            "(metric * 100).tagNotEqual('mode', 'idle').sum(['host']).rate('PT1M')");
-        assertNotNull(java);
-        assertTrue(java.contains(".multiply(100)"),
-            "Should have multiply inside parens");
-        assertTrue(java.contains(".tagNotEqual(\"mode\", \"idle\")"),
-            "Should chain tagNotEqual after parens");
-        assertTrue(java.contains(".rate(\"PT1M\")"),
-            "Should chain rate at the end");
-    }
-
-    // ---- tag() Closure — Simple Cases ----
-
-    @Test
-    void tagAssignmentWithStringConcat() {
-        final String java = transpiler.transpileExpression("MalExpr_test",
-            "metric.tag({tags -> tags.route = 'route/' + tags['route']})");
-        assertNotNull(java);
-
-        assertTrue(java.contains(".tag((TagFunction)"),
-            "Should cast closure to TagFunction");
-        assertTrue(java.contains("tags.put(\"route\", \"route/\" + tags.get(\"route\"))"),
-            "Should translate assignment with string concat and subscript read");
-        assertTrue(java.contains("return tags;"),
-            "Should return tags at end of lambda");
-    }
-
-    @Test
-    void tagRemove() {
-        final String java = transpiler.transpileExpression("MalExpr_test",
-            "metric.tag({tags -> tags.remove('condition')})");
-        assertNotNull(java);
-
-        assertTrue(java.contains("tags.remove(\"condition\")"),
-            "Should translate remove call");
-    }
-
-    @Test
-    void tagPropertyToProperty() {
-        final String java = transpiler.transpileExpression("MalExpr_test",
-            "metric.tag({tags -> tags.rs_nm = tags.set})");
-        assertNotNull(java);
-
-        assertTrue(java.contains("tags.put(\"rs_nm\", tags.get(\"set\"))"),
-            "Should translate property read on RHS to tags.get()");
-    }
-
-    @Test
-    void tagStringConcatWithPropertyRead() {
-        final String java = transpiler.transpileExpression("MalExpr_test",
-            "metric.tag({tags -> tags.cluster = 'es::' + tags.cluster})");
-        assertNotNull(java);
-
-        assertTrue(java.contains("tags.put(\"cluster\", \"es::\" + tags.get(\"cluster\"))"),
-            "Should translate string concat with property read");
-    }
-
-    @Test
-    void tagClosureLambdaStructure() {
-        final String java = transpiler.transpileExpression("MalExpr_test",
-            "metric.tag({tags -> tags.x = 'y'})");
-        assertNotNull(java);
-
-        assertTrue(java.contains("(tags -> {"),
-            "Should have lambda opening");
-        assertTrue(java.contains("return tags;"),
-            "Should return tags variable");
-    }
-
-    @Test
-    void tagWithSubscriptWrite() {
-        final String java = transpiler.transpileExpression("MalExpr_test",
-            "metric.tag({tags -> tags['service_name'] = tags['svc']})");
-        assertNotNull(java);
-
-        assertTrue(java.contains("tags.put(\"service_name\", tags.get(\"svc\"))"),
-            "Should translate subscript write and read");
-    }
-
-    @Test
-    void tagChainAfterTag() {
-        final String java = transpiler.transpileExpression("MalExpr_test",
-            "metric.tag({tags -> tags.x = 'y'}).sum(['host']).service(['svc'], Layer.GENERAL)");
-        assertNotNull(java);
-
-        assertTrue(java.contains(".tag((TagFunction)"),
-            "Should have tag call");
-        assertTrue(java.contains(".sum(List.of(\"host\"))"),
-            "Should chain sum after tag");
-        assertTrue(java.contains(".service(List.of(\"svc\"), Layer.GENERAL)"),
-            "Should chain service after sum");
-    }
-
-    // ---- tag() Closure — if/else + Compound Conditions ----
-
-    @Test
-    void ifOnlyWithChainedOr() {
-        final String java = transpiler.transpileExpression("MalExpr_test",
-            "metric.tag({tags -> if (tags['gc'] == 'PS Scavenge' || tags['gc'] == 'Copy' || tags['gc'] == 'ParNew' || tags['gc'] == 'G1 Young Generation') {tags.gc = 'young_gc_count'} })");
-        assertNotNull(java);
-
-        assertTrue(java.contains("if (\"PS Scavenge\".equals(tags.get(\"gc\"))"),
-            "Should translate first == with constant on left for null-safety");
-        assertTrue(java.contains("|| \"Copy\".equals(tags.get(\"gc\"))"),
-            "Should chain || for second comparison");
-        assertTrue(java.contains("|| \"ParNew\".equals(tags.get(\"gc\"))"),
-            "Should chain || for third comparison");
-        assertTrue(java.contains("|| \"G1 Young Generation\".equals(tags.get(\"gc\"))"),
-            "Should chain || for fourth comparison");
-        assertTrue(java.contains("tags.put(\"gc\", \"young_gc_count\")"),
-            "Should translate assignment in if body");
-    }
-
-    @Test
-    void ifElse() {
-        final String java = transpiler.transpileExpression("MalExpr_test",
-            "metric.tag({tags -> if (tags['primary'] == 'true') {tags.primary = 'primary'} else {tags.primary = 'replica'} })");
-        assertNotNull(java);
-
-        assertTrue(java.contains("if (\"true\".equals(tags.get(\"primary\"))"),
-            "Should translate == comparison in condition");
-        assertTrue(java.contains("tags.put(\"primary\", \"primary\")"),
-            "Should translate if-branch assignment");
-        assertTrue(java.contains("} else {"),
-            "Should have else clause");
-        assertTrue(java.contains("tags.put(\"primary\", \"replica\")"),
-            "Should translate else-branch assignment");
-    }
-
-    @Test
-    void ifOnlyNoElse() {
-        final String java = transpiler.transpileExpression("MalExpr_test",
-            "metric.tag({tags -> if (tags['level'] == '1') {tags.level = 'L1 aggregation'} })");
-        assertNotNull(java);
-
-        assertTrue(java.contains("if (\"1\".equals(tags.get(\"level\"))"),
-            "Should translate condition");
-        assertTrue(java.contains("tags.put(\"level\", \"L1 aggregation\")"),
-            "Should translate if-body");
-        assertTrue(!java.contains("else"),
-            "Should NOT have else clause");
-    }
-
-    @Test
-    void notEqualComparison() {
-        final String java = transpiler.transpileExpression("MalExpr_test",
-            "metric.tag({tags -> if (tags['status'] != 'ok') {tags.status = 'error'} })");
-        assertNotNull(java);
-
-        assertTrue(java.contains("!\"ok\".equals(tags.get(\"status\"))"),
-            "Should translate != with negated .equals()");
-    }
-
-    @Test
-    void logicalAndCondition() {
-        final String java = transpiler.transpileExpression("MalExpr_test",
-            "metric.tag({tags -> if (tags['a'] == 'x' && tags['b'] == 'y') {tags.c = 'z'} })");
-        assertNotNull(java);
-
-        assertTrue(java.contains("\"x\".equals(tags.get(\"a\")) && \"y\".equals(tags.get(\"b\"))"),
-            "Should translate && with .equals() on both sides");
-    }
-
-    @Test
-    void chainedTagClosuresWithIf() {
-        final String java = transpiler.transpileExpression("MalExpr_test",
-            "metric.tag({tags -> if (tags['level'] == '1') {tags.level = 'L1 aggregation'} })" +
-            ".tag({tags -> if (tags['level'] == '2') {tags.level = 'L2 aggregation'} })");
-        assertNotNull(java);
-
-        assertTrue(java.contains("\"1\".equals(tags.get(\"level\"))"),
-            "Should translate first tag closure condition");
-        assertTrue(java.contains("\"2\".equals(tags.get(\"level\"))"),
-            "Should translate second tag closure condition");
-    }
-
-    @Test
-    void ifWithMethodChainAfter() {
-        final String java = transpiler.transpileExpression("MalExpr_test",
-            "metric.tag({tags -> if (tags['gc'] == 'Copy') {tags.gc = 'young'} }).sum(['host']).service(['svc'], Layer.GENERAL)");
-        assertNotNull(java);
-
-        assertTrue(java.contains(".tag((TagFunction)"),
-            "Should have TagFunction cast");
-        assertTrue(java.contains("\"Copy\".equals(tags.get(\"gc\"))"),
-            "Should have if condition");
-        assertTrue(java.contains(".sum(List.of(\"host\"))"),
-            "Should chain sum after tag");
-    }
-
-    // ---- Filter Closures ----
-
-    @Test
-    void simpleEqualityFilter() {
-        final String java = transpiler.transpileFilter("MalFilter_0",
-            "{ tags -> tags.job_name == 'vm-monitoring' }");
-        assertNotNull(java);
-
-        assertTrue(java.contains("public class MalFilter_0 implements MalFilter"),
-            "Should implement MalFilter");
-        assertTrue(java.contains("public boolean test(Map<String, String> tags)"),
-            "Should have test method");
-        assertTrue(java.contains("\"vm-monitoring\".equals(tags.get(\"job_name\"))"),
-            "Should translate == with constant on left");
-    }
-
-    @Test
-    void filterPackageAndImports() {
-        final String java = transpiler.transpileFilter("MalFilter_0",
-            "{ tags -> tags.job_name == 'x' }");
-        assertNotNull(java);
-
-        assertTrue(java.contains("package " + MalToJavaTranspiler.GENERATED_PACKAGE),
-            "Should have correct package");
-        assertTrue(java.contains("import java.util.*;"),
-            "Should import java.util");
-        assertTrue(java.contains("import org.apache.skywalking.oap.meter.analyzer.dsl.*;"),
-            "Should import dsl package");
-    }
-
-    @Test
-    void orFilter() {
-        final String java = transpiler.transpileFilter("MalFilter_1",
-            "{ tags -> tags.job_name == 'flink-jobManager-monitoring' || tags.job_name == 'flink-taskManager-monitoring' }");
-        assertNotNull(java);
-
-        assertTrue(java.contains("\"flink-jobManager-monitoring\".equals(tags.get(\"job_name\"))"),
-            "Should translate first ==");
-        assertTrue(java.contains("|| \"flink-taskManager-monitoring\".equals(tags.get(\"job_name\"))"),
-            "Should translate || with second ==");
-    }
-
-    @Test
-    void inListFilter() {
-        final String java = transpiler.transpileFilter("MalFilter_2",
-            "{ tags -> tags.job_name in ['kubernetes-cadvisor', 'kube-state-metrics'] }");
-        assertNotNull(java);
-
-        assertTrue(java.contains("List.of(\"kubernetes-cadvisor\", \"kube-state-metrics\").contains(tags.get(\"job_name\"))"),
-            "Should translate 'in' to List.of().contains()");
-    }
-
-    @Test
-    void compoundAndFilter() {
-        final String java = transpiler.transpileFilter("MalFilter_3",
-            "{ tags -> tags.cloud_provider == 'aws' && tags.Namespace == 'AWS/S3' }");
-        assertNotNull(java);
-
-        assertTrue(java.contains("\"aws\".equals(tags.get(\"cloud_provider\"))"),
-            "Should translate first ==");
-        assertTrue(java.contains("&& \"AWS/S3\".equals(tags.get(\"Namespace\"))"),
-            "Should translate && with second ==");
-    }
-
-    @Test
-    void truthinessFilter() {
-        final String java = transpiler.transpileFilter("MalFilter_4",
-            "{ tags -> tags.cloud_provider == 'aws' && tags.Stage }");
-        assertNotNull(java);
-
-        assertTrue(java.contains("\"aws\".equals(tags.get(\"cloud_provider\"))"),
-            "Should translate ==");
-        assertTrue(java.contains("(tags.get(\"Stage\") != null && !tags.get(\"Stage\").isEmpty())"),
-            "Should translate bare tags.Stage as truthiness check");
-    }
-
-    @Test
-    void negatedTruthinessFilter() {
-        final String java = transpiler.transpileFilter("MalFilter_5",
-            "{ tags -> tags.cloud_provider == 'aws' && !tags.Method }");
-        assertNotNull(java);
-
-        assertTrue(java.contains("(tags.get(\"Method\") == null || tags.get(\"Method\").isEmpty())"),
-            "Should translate !tags.Method as negated truthiness");
-    }
-
-    @Test
-    void compoundWithTruthinessAndNegation() {
-        final String java = transpiler.transpileFilter("MalFilter_6",
-            "{ tags -> tags.cloud_provider == 'aws' && tags.Namespace == 'AWS/ApiGateway' && tags.Stage && !tags.Method }");
-        assertNotNull(java);
-
-        assertTrue(java.contains("\"aws\".equals(tags.get(\"cloud_provider\"))"),
-            "Should translate first ==");
-        assertTrue(java.contains("\"AWS/ApiGateway\".equals(tags.get(\"Namespace\"))"),
-            "Should translate second ==");
-        assertTrue(java.contains("(tags.get(\"Stage\") != null && !tags.get(\"Stage\").isEmpty())"),
-            "Should translate truthiness");
-        assertTrue(java.contains("(tags.get(\"Method\") == null || tags.get(\"Method\").isEmpty())"),
-            "Should translate negated truthiness");
-    }
-
-    @Test
-    void wrappedBlockFilter() {
-        final String java = transpiler.transpileFilter("MalFilter_7",
-            "{ tags -> {tags.cloud_provider == 'aws' && tags.Namespace == 'AWS/S3'} }");
-        assertNotNull(java);
-
-        assertTrue(java.contains("\"aws\".equals(tags.get(\"cloud_provider\"))"),
-            "Should unwrap inner block and translate ==");
-        assertTrue(java.contains("\"AWS/S3\".equals(tags.get(\"Namespace\"))"),
-            "Should translate second == after unwrapping");
-    }
-
-    @Test
-    void truthinessWithOrInParens() {
-        final String java = transpiler.transpileFilter("MalFilter_8",
-            "{ tags -> tags.cloud_provider == 'aws' && (tags.ApiId || tags.ApiName) }");
-        assertNotNull(java);
-
-        assertTrue(java.contains("(tags.get(\"ApiId\") != null && !tags.get(\"ApiId\").isEmpty())"),
-            "Should translate ApiId truthiness");
-        assertTrue(java.contains("(tags.get(\"ApiName\") != null && !tags.get(\"ApiName\").isEmpty())"),
-            "Should translate ApiName truthiness");
-    }
-
-    // ---- forEach() Closure ----
-
-    @Test
-    void forEachBasicStructure() {
-        final String java = transpiler.transpileExpression("MalExpr_test",
-            "metric.forEach(['client', 'server'], { prefix, tags -> tags[prefix + '_id'] = 'test' })");
-        assertNotNull(java);
-
-        assertTrue(java.contains(".forEach(List.of(\"client\", \"server\"), (ForEachFunction)"),
-            "Should cast closure to ForEachFunction");
-        assertTrue(java.contains("(prefix, tags) -> {"),
-            "Should have two-parameter lambda");
-        assertTrue(java.contains("tags.put(prefix + \"_id\", \"test\")"),
-            "Should translate dynamic subscript write");
-    }
-
-    @Test
-    void forEachNullCheckWithEarlyReturn() {
-        final String java = transpiler.transpileExpression("MalExpr_test",
-            "metric.forEach(['client'], { prefix, tags -> if (tags[prefix + '_process_id'] != null) { return } })");
-        assertNotNull(java);
-
-        assertTrue(java.contains("tags.get(prefix + \"_process_id\") != null"),
-            "Should translate null check");
-        assertTrue(java.contains("return;"),
-            "Should have void return for early exit");
-    }
-
-    @Test
-    void forEachWithProcessRegistry() {
-        final String java = transpiler.transpileExpression("MalExpr_test",
-            "metric.forEach(['client'], { prefix, tags -> " +
-            "tags[prefix + '_process_id'] = ProcessRegistry.generateVirtualLocalProcess(tags.service, tags.instance) })");
-        assertNotNull(java);
-
-        assertTrue(java.contains("ProcessRegistry.generateVirtualLocalProcess(tags.get(\"service\"), tags.get(\"instance\"))"),
-            "Should translate static method call with tag reads as args");
-        assertTrue(java.contains("tags.put(prefix + \"_process_id\","),
-            "Should translate dynamic subscript write");
-    }
-
-    @Test
-    void forEachVarDeclaration() {
-        final String java = transpiler.transpileExpression("MalExpr_test",
-            "metric.forEach(['component'], { key, tags -> String result = '' })");
-        assertNotNull(java);
-
-        assertTrue(java.contains("String result = \"\""),
-            "Should translate variable declaration with empty string");
-    }
-
-    @Test
-    void forEachVarDeclWithTagRead() {
-        final String java = transpiler.transpileExpression("MalExpr_test",
-            "metric.forEach(['component'], { key, tags -> String protocol = tags['protocol'] })");
-        assertNotNull(java);
-
-        assertTrue(java.contains("String protocol = tags.get(\"protocol\")"),
-            "Should translate var decl with tag read");
-    }
-
-    @Test
-    void forEachIfElseIfChain() {
-        final String java = transpiler.transpileExpression("MalExpr_test",
-            "metric.forEach(['component'], { key, tags -> " +
-            "String protocol = tags['protocol']\n" +
-            "String ssl = tags['is_ssl']\n" +
-            "String result = ''\n" +
-            "if (protocol == 'http' && ssl == 'true') { result = '129' } " +
-            "else if (protocol == 'http') { result = '49' } " +
-            "else if (ssl == 'true') { result = '130' } " +
-            "else { result = '110' }\n" +
-            "tags[key] = result })");
-        assertNotNull(java);
-
-        assertTrue(java.contains("String protocol = tags.get(\"protocol\")"),
-            "Should declare protocol");
-        assertTrue(java.contains("String ssl = tags.get(\"is_ssl\")"),
-            "Should declare ssl");
-
-        assertTrue(java.contains("\"http\".equals(protocol)"),
-            "Should compare local var with .equals()");
-        assertTrue(java.contains("\"true\".equals(ssl)"),
-            "Should compare ssl with .equals()");
-
-        assertTrue(java.contains("} else if ("),
-            "Should produce else-if, not nested else { if }");
-
-        assertTrue(java.contains("} else {"),
-            "Should have final else");
-        assertTrue(java.contains("result = \"110\""),
-            "Should assign default value in else");
-
-        assertTrue(java.contains("tags.put(key, result)"),
-            "Should write result to tags[key]");
-    }
-
-    @Test
-    void forEachLocalVarAssignment() {
-        final String java = transpiler.transpileExpression("MalExpr_test",
-            "metric.forEach(['x'], { key, tags -> " +
-            "String r = ''\n" +
-            "r = 'abc'\n" +
-            "tags[key] = r })");
-        assertNotNull(java);
-
-        assertTrue(java.contains("r = \"abc\""),
-            "Should translate local var reassignment");
-    }
-
-    @Test
-    void forEachEqualsOnStringComparison() {
-        final String java = transpiler.transpileExpression("MalExpr_test",
-            "metric.forEach(['client'], { prefix, tags -> " +
-            "if (tags[prefix + '_local'] == 'true') { tags[prefix + '_id'] = 'local' } })");
-        assertNotNull(java);
-
-        assertTrue(java.contains("\"true\".equals(tags.get(prefix + \"_local\"))"),
-            "Should translate dynamic subscript comparison with .equals()");
-        assertTrue(java.contains("tags.put(prefix + \"_id\", \"local\")"),
-            "Should translate dynamic subscript assignment");
-    }
-
-    @Test
-    void chainedForEach() {
-        final String java = transpiler.transpileExpression("MalExpr_test",
-            "metric.forEach(['a'], { k1, tags -> tags[k1] = 'x' })" +
-            ".forEach(['b'], { k2, tags -> tags[k2] = 'y' })");
-        assertNotNull(java);
-
-        assertTrue(java.contains("(ForEachFunction) (k1, tags)"),
-            "Should have first forEach with k1");
-        assertTrue(java.contains("(ForEachFunction) (k2, tags)"),
-            "Should have second forEach with k2");
-    }
-
-    // ---- Elvis (?:), Safe Navigation (?.), Ternary (? :) ----
-
-    @Test
-    void safeNavigation() {
-        final String java = transpiler.transpileExpression("MalExpr_test",
-            "metric.tag({tags -> tags.svc = tags['skywalking_service']?.trim() })");
-        assertNotNull(java);
-
-        assertTrue(java.contains("(tags.get(\"skywalking_service\") != null ? tags.get(\"skywalking_service\").trim() : null)"),
-            "Should translate ?.trim() to null-checked call");
-    }
-
-    @Test
-    void elvisOperator() {
-        final String java = transpiler.transpileExpression("MalExpr_test",
-            "metric.tag({tags -> tags.svc = tags['name'] ?: 'unknown' })");
-        assertNotNull(java);
-
-        assertTrue(java.contains("(tags.get(\"name\") != null ? tags.get(\"name\") : \"unknown\")"),
-            "Should translate ?: to null-check with default");
-    }
-
-    @Test
-    void safeNavPlusElvis() {
-        final String java = transpiler.transpileExpression("MalExpr_test",
-            "metric.tag({tags -> tags.service_name = 'APISIX::'+(tags['skywalking_service']?.trim()?:'APISIX') })");
-        assertNotNull(java);
-
-        assertTrue(java.contains("tags.get(\"skywalking_service\") != null ? tags.get(\"skywalking_service\").trim() : null"),
-            "Should have safe nav for trim");
-        assertTrue(java.contains("!= null ?") && java.contains(": \"APISIX\""),
-            "Should have elvis default to APISIX");
-        assertTrue(java.contains("\"APISIX::\" + "),
-            "Should have string prefix concatenation");
-    }
-
-    @Test
-    void ternaryOperator() {
-        final String java = transpiler.transpileExpression("MalExpr_test",
-            "metric.tag({tags -> tags.service_name = tags.ApiId ? 'gw::'+tags.ApiId : 'gw::'+tags.ApiName })");
-        assertNotNull(java);
-
-        assertTrue(java.contains("tags.get(\"ApiId\") != null && !tags.get(\"ApiId\").isEmpty()"),
-            "Should translate ternary condition as truthiness check");
-        assertTrue(java.contains("\"gw::\" + tags.get(\"ApiId\")"),
-            "Should have true branch expression");
-        assertTrue(java.contains("\"gw::\" + tags.get(\"ApiName\")"),
-            "Should have false branch expression");
-    }
-
-    @Test
-    void safeNavInFilterCondition() {
-        final String java = transpiler.transpileFilter("MalFilter_test",
-            "{ tags -> tags.job_name == 'eks-monitoring' && tags.Service?.trim() }");
-        assertNotNull(java);
-
-        assertTrue(java.contains("\"eks-monitoring\".equals(tags.get(\"job_name\"))"),
-            "Should translate == comparison");
-        assertTrue(java.contains("tags.get(\"Service\") != null ? tags.get(\"Service\").trim() : null"),
-            "Should have safe nav for Service?.trim()");
-    }
-
-    // ---- instance() with PropertiesExtractor, MapExpression ----
-
-    @Test
-    void instanceWithPropertiesExtractor() {
-        final String java = transpiler.transpileExpression("MalExpr_test",
-            "metric.instance(['cluster', 'service'], '::', ['pod'], '', Layer.K8S_SERVICE, " +
-            "{tags -> ['pod': tags.pod, 'namespace': tags.namespace]})");
-        assertNotNull(java);
-
-        assertTrue(java.contains(".instance("),
-            "Should have instance call");
-        assertTrue(java.contains("(PropertiesExtractor)"),
-            "Should cast closure to PropertiesExtractor");
-        assertTrue(java.contains("Map.of(\"pod\", tags.get(\"pod\"), \"namespace\", tags.get(\"namespace\"))"),
-            "Should translate map literal to Map.of()");
-    }
-
-    @Test
-    void mapExpressionInTagValue() {
-        final String java = transpiler.transpileExpression("MalExpr_test",
-            "metric.instance(['svc'], ['inst'], Layer.GENERAL, {tags -> ['key': tags.val]})");
-        assertNotNull(java);
-
-        assertTrue(java.contains("Map.of(\"key\", tags.get(\"val\"))"),
-            "Should translate single-entry map");
-        assertTrue(java.contains("(PropertiesExtractor)"),
-            "Should have PropertiesExtractor cast");
-    }
-
-    @Test
-    void processRelationNoClosures() {
-        final String java = transpiler.transpileExpression("MalExpr_test",
-            "metric.processRelation('side', ['service'], ['instance'], " +
-            "'client_process_id', 'server_process_id', 'component')");
-        assertNotNull(java);
-
-        assertTrue(java.contains(".processRelation(\"side\", List.of(\"service\"), List.of(\"instance\"), " +
-            "\"client_process_id\", \"server_process_id\", \"component\")"),
-            "Should translate processRelation as regular method call");
-    }
-
-    // ---- Compilation + Manifests ----
-
-    @Test
-    void sourceWrittenForCompilation(@TempDir Path tempDir) throws Exception {
-        final String source = transpiler.transpileExpression("MalExpr_compile_test",
-            "metric.sum(['host']).service(['svc'], Layer.GENERAL)");
-        transpiler.registerExpression("MalExpr_compile_test", source);
-
-        final File sourceDir = tempDir.resolve("src").toFile();
-        final File outputDir = tempDir.resolve("classes").toFile();
-
-        try {
-            transpiler.compileAll(sourceDir, outputDir, System.getProperty("java.class.path"));
-
-            final String classPath = MalToJavaTranspiler.GENERATED_PACKAGE.replace('.', File.separatorChar)
-                + File.separator + "MalExpr_compile_test.class";
-            assertTrue(new File(outputDir, classPath).exists(),
-                "Compiled .class file should exist");
-        } catch (RuntimeException e) {
-            if (e.getMessage().contains("compilation failed")) {
-                final String pkgPath = MalToJavaTranspiler.GENERATED_PACKAGE.replace('.', File.separatorChar);
-                final File javaFile = new File(sourceDir, pkgPath + "/MalExpr_compile_test.java");
-                assertTrue(javaFile.exists(), "Source .java file should be written");
-                final String written = Files.readString(javaFile.toPath());
-                assertTrue(written.contains("implements MalExpression"),
-                    "Written source should implement MalExpression");
-            } else {
-                throw e;
-            }
-        }
-    }
-
-    @Test
-    void expressionManifest(@TempDir Path tempDir) throws Exception {
-        transpiler.registerExpression("MalExpr_a",
-            transpiler.transpileExpression("MalExpr_a", "metric_a"));
-        transpiler.registerExpression("MalExpr_b",
-            transpiler.transpileExpression("MalExpr_b", "metric_b"));
-
-        final File outputDir = tempDir.toFile();
-        transpiler.writeExpressionManifest(outputDir);
-
-        final File manifest = new File(outputDir, "META-INF/mal-expressions.txt");
-        assertTrue(manifest.exists(), "Manifest file should exist");
-
-        final List<String> lines = Files.readAllLines(manifest.toPath());
-        assertTrue(lines.contains(MalToJavaTranspiler.GENERATED_PACKAGE + ".MalExpr_a"),
-            "Should contain MalExpr_a FQCN");
-        assertTrue(lines.contains(MalToJavaTranspiler.GENERATED_PACKAGE + ".MalExpr_b"),
-            "Should contain MalExpr_b FQCN");
-    }
-
-    @Test
-    void filterManifest(@TempDir Path tempDir) throws Exception {
-        final String literal = "{ tags -> tags.job == 'x' }";
-        transpiler.registerFilter("MalFilter_0", literal,
-            transpiler.transpileFilter("MalFilter_0", literal));
-
-        final File outputDir = tempDir.toFile();
-        transpiler.writeFilterManifest(outputDir);
-
-        final File manifest = new File(outputDir, "META-INF/mal-filter-expressions.properties");
-        assertTrue(manifest.exists(), "Filter manifest should exist");
-
-        final String content = Files.readString(manifest.toPath());
-        assertTrue(content.contains(MalToJavaTranspiler.GENERATED_PACKAGE + ".MalFilter_0"),
-            "Should contain MalFilter_0 FQCN");
-    }
-}
diff --git a/oap-server/analyzer/meter-analyzer-v2/pom.xml b/oap-server/analyzer/meter-analyzer-v2/pom.xml
deleted file mode 100644
index 8bbcaa43ed..0000000000
--- a/oap-server/analyzer/meter-analyzer-v2/pom.xml
+++ /dev/null
@@ -1,78 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-  ~ Licensed to the Apache Software Foundation (ASF) under one or more
-  ~ contributor license agreements.  See the NOTICE file distributed with
-  ~ this work for additional information regarding copyright ownership.
-  ~ The ASF licenses this file to You under the Apache License, Version 2.0
-  ~ (the "License"); you may not use this file except in compliance with
-  ~ the License.  You may obtain a copy of the License at
-  ~
-  ~     http://www.apache.org/licenses/LICENSE-2.0
-  ~
-  ~ Unless required by applicable law or agreed to in writing, software
-  ~ distributed under the License is distributed on an "AS IS" BASIS,
-  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  ~ See the License for the specific language governing permissions and
-  ~ limitations under the License.
-  ~
-  -->
-
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
-    <parent>
-        <artifactId>analyzer</artifactId>
-        <groupId>org.apache.skywalking</groupId>
-        <version>${revision}</version>
-    </parent>
-    <modelVersion>4.0.0</modelVersion>
-
-    <artifactId>meter-analyzer-v2</artifactId>
-    <description>Pure Java MAL runtime that loads transpiled MalExpression/MalFilter classes from manifests instead of Groovy</description>
-
-    <dependencies>
-        <dependency>
-            <groupId>org.apache.skywalking</groupId>
-            <artifactId>meter-analyzer</artifactId>
-            <version>${project.version}</version>
-        </dependency>
-    </dependencies>
-
-    <build>
-        <plugins>
-            <plugin>
-                <groupId>org.apache.maven.plugins</groupId>
-                <artifactId>maven-shade-plugin</artifactId>
-                <executions>
-                    <execution>
-                        <phase>package</phase>
-                        <goals>
-                            <goal>shade</goal>
-                        </goals>
-                        <configuration>
-                            <createDependencyReducedPom>true</createDependencyReducedPom>
-                            <artifactSet>
-                                <includes>
-                                    <include>org.apache.skywalking:meter-analyzer</include>
-                                </includes>
-                            </artifactSet>
-                            <filters>
-                                <filter>
-                                    <artifact>org.apache.skywalking:meter-analyzer</artifact>
-                                    <excludes>
-                                        <exclude>org/apache/skywalking/oap/meter/analyzer/dsl/DSL.class</exclude>
-                                        <exclude>org/apache/skywalking/oap/meter/analyzer/dsl/DSL$*.class</exclude>
-                                        <exclude>org/apache/skywalking/oap/meter/analyzer/dsl/Expression.class</exclude>
-                                        <exclude>org/apache/skywalking/oap/meter/analyzer/dsl/Expression$*.class</exclude>
... 8272 lines suppressed ...