DEV Community

vinaykumar0339
vinaykumar0339

Posted on

JexLang: Write Once, Validate Everywhere β€” A Platform-Agnostic Expression Language

🎯 The Problem We All Face

Picture this: You're building a multi-platform application. A React web app, an Android mobile app, a Spring Boot backend, and maybe an iOS app in the pipeline. Everything seems manageable until you hit a familiar wall β€” validation logic.

Your product manager walks in and says:

"We need to validate that users are 18+ years old, their email matches the company domain, and if they select 'Premium' plan, they must have a valid credit card."

Simple enough, right? Now multiply that across platforms:

// JavaScript (Web/React Native/NodeJS)
if (user.age >= 18 && user.email.endsWith('@company.com') && 
    (user.plan !== 'Premium' || user.hasValidCard)) {
  // valid
}
Enter fullscreen mode Exit fullscreen mode
// Java (Backend/Android)
if (user.getAge() >= 18 && user.getEmail().endsWith("@company.com") && 
    (!user.getPlan().equals("Premium") || user.hasValidCard())) {
  // valid
}
Enter fullscreen mode Exit fullscreen mode
// Swift (iOS)
if user.age >= 18 && user.email.hasSuffix("@company.com") && 
    (user.plan != "Premium" || user.hasValidCard) {
  // valid
}
Enter fullscreen mode Exit fullscreen mode

Now imagine you have 50+ validation rules across your application. Each platform has its own implementation. Each implementation needs to be tested. Each implementation can have subtle bugs.

Welcome to validation hell. πŸ”₯


😫 The Real Pain Points

1. Code Duplication Nightmare

Every validation rule exists in multiple places. When a business rule changes, you're updating code in 3-4 different repositories. Miss one? Congratulations, you now have inconsistent behavior that will haunt your debugging sessions.

2. Subtle Platform Differences

Each language handles things slightly differently:

  • String comparison: === vs .equals() vs ==
  • Null handling: undefined vs null vs Optional<>
  • Type coercion: JavaScript's quirky "5" + 3 = "53" vs Java's strict typing

These subtle differences lead to bugs that only appear on specific platforms β€” the worst kind of bugs.

3. Hardcoded Logic = Deployment Pain

Need to change a validation rule? That's a code change. Code change means PR review, testing, staging, and deployment. For each platform. Separately.

What if the validation logic could live in your database or configuration? What if non-developers could modify simple rules without touching code?

4. Testing Overhead

50 validation rules Γ— 4 platforms = 200 test cases (at minimum). And you need to ensure they all behave identically. Good luck with that.


πŸ’‘ What If There Was a Better Way?

What if you could write your validation logic once in a simple, familiar syntax:

user.age >= 18 && endsWith(user.email, "@company.com") && (user.plan != "Premium" || user.hasValidCard)
Enter fullscreen mode Exit fullscreen mode

And have it execute identically on:

  • βœ… JavaScript/TypeScript (Browser & Node.js)
  • βœ… Java (Android & Backend)
  • 🚧 Swift (iOS) β€” coming soon!

No platform-specific quirks. No code duplication. No deployment overhead.

This is exactly why I built JexLang.


πŸš€ Introducing JexLang

JexLang is a lightweight, platform-agnostic expression language with JavaScript-like syntax. It's designed to solve one problem exceptionally well: write logic once, run it everywhere with consistent behavior.

Why JavaScript-Like Syntax?

Let's be honest β€” most developers are familiar with JavaScript syntax. Even if you're primarily a Java or Swift developer, you've likely encountered JavaScript at some point. By using a familiar syntax, JexLang has virtually zero learning curve.

If you can write this:

user.age >= 18 && user.active === true
Enter fullscreen mode Exit fullscreen mode

You already know JexLang.


πŸ› οΈ How JexLang Works

JexLang consists of three core components:

1. Lexer (Tokenizer)

Breaks down your expression into tokens:

"user.age >= 18" β†’ [IDENTIFIER:user, DOT, IDENTIFIER:age, GTE, NUMBER:18]
Enter fullscreen mode Exit fullscreen mode

2. Parser

Converts tokens into an Abstract Syntax Tree (AST):

BinaryExpression {
  left: MemberExpression { object: "user", property: "age" },
  operator: ">=",
  right: NumericLiteral { value: 18 }
}
Enter fullscreen mode Exit fullscreen mode

3. Interpreter

Evaluates the AST against your context data and returns the result.

The magic? The same AST structure and evaluation logic is implemented identically in each language. Same parser rules. Same operator precedence. Same type coercion. Same results.


πŸ“– Real-World Use Cases

1. Dynamic Form Validations

Store validation rules in your backend and share them across all platforms:

{
  "fields": [
    {
      "name": "email",
      "validations": [
        {
          "rule": "required(email)",
          "message": "Email is required"
        },
        {
          "rule": "contains(email, '@')",
          "message": "Please enter a valid email"
        }
      ]
    },
    {
      "name": "age",
      "validations": [
        {
          "rule": "age >= 18",
          "message": "You must be 18 or older"
        }
      ]
    }
  ]
}
Enter fullscreen mode Exit fullscreen mode

Your React app, Android app, and backend all fetch these rules and evaluate them using JexLang. One source of truth.

2. Business Rule Engines

Configure business logic without code deployments:

// Rules stored in database
const discountRules = [
  { condition: "customer.tier === 'Gold' && cart.total > 100", discount: 20 },
  { condition: "customer.tier === 'Silver' && cart.total > 200", discount: 10 },
  { condition: "cart.items.length > 5", discount: 5 }
];

// Evaluate at runtime
const applicableRule = discountRules.find(rule => 
  jexlang.evaluate(rule.condition, { customer, cart })
);
Enter fullscreen mode Exit fullscreen mode

Marketing team wants to change the Gold tier threshold from 100 to 150? Update the database. No deployment needed.

3. Conditional UI Rendering

Show/hide UI elements based on configurable rules:

{
  "component": "PremiumBanner",
  "showWhen": "user.subscription === 'free' && user.daysActive > 30"
}
Enter fullscreen mode Exit fullscreen mode

4. Workflow Automation

Build workflow engines where conditions are user-configurable:

IF order.total > 1000 AND customer.verified === true
  β†’ Route to priority fulfillment

IF order.items.length > 10
  β†’ Apply bulk processing

IF contains(order.shippingAddress.country, 'EU')
  β†’ Apply VAT calculation
Enter fullscreen mode Exit fullscreen mode

5. Feature Flags with Complex Logic

Go beyond simple boolean flags:

const featureRules = {
  "newCheckout": "user.region === 'US' && user.accountAge > 30 && !user.hasActiveIssues",
  "betaFeatures": "user.role === 'developer' || contains(user.email, '@internal.com')"
};
Enter fullscreen mode Exit fullscreen mode

⚑ Quick Examples

Basic Expressions

// Arithmetic
jexlang.evaluate("10 + 5 * 2")  // 20

// Comparisons
jexlang.evaluate("100 >= 50")  // true

// Logical operators
jexlang.evaluate("true && false || true")  // true

// String operations
jexlang.evaluate("'Hello' + ' ' + 'World'")  // "Hello World"
Enter fullscreen mode Exit fullscreen mode

Working with Context

const context = {
  user: {
    name: "John Doe",
    age: 28,
    email: "john@company.com",
    roles: ["admin", "editor"]
  },
  settings: {
    maxUploadSize: 100,
    allowedFormats: ["jpg", "png", "pdf"]
  }
};

// Access nested properties
jexlang.evaluate("user.name", context)  // "John Doe"

// Complex conditions
jexlang.evaluate("user.age >= 18 && contains(user.email, '@company.com')", context)  // true
Enter fullscreen mode Exit fullscreen mode

Built-in Functions

// String functions
jexlang.evaluate("length('Hello')")  // 5
jexlang.evaluate("upper('hello')")  // "HELLO"
jexlang.evaluate("startsWith('Hello World', 'Hello')")  // true

// Array functions
jexlang.evaluate("length(items)", { items: [1, 2, 3] })  // 3

// Type checking
jexlang.evaluate("isNumber(42)")  // true
jexlang.evaluate("isString('hello')")  // true
Enter fullscreen mode Exit fullscreen mode

Ternary Expressions

jexlang.evaluate("user.age >= 18 ? 'Adult' : 'Minor'", { user: { age: 25 } })  // "Adult"
Enter fullscreen mode Exit fullscreen mode

πŸ”§ Getting Started

JavaScript/TypeScript

Installation:

npm install jexlang-ts
Enter fullscreen mode Exit fullscreen mode

Usage:

import { JexEvaluator } from "jexlang-ts";

const jexlang = new JexEvaluator();

// Simple expression
const result = jexlang.evaluate("1 + 2 * 3");  // 7

// With context
const context = { user: { name: "John", age: 30 } };
const isAdult = jexlang.evaluate("user.age >= 18", context);  // true
Enter fullscreen mode Exit fullscreen mode

Java

Gradle:

implementation 'io.github.vinaykumar0339:jexlang-java:1.0.0'
Enter fullscreen mode Exit fullscreen mode

Maven:

<dependency>
    <groupId>io.github.vinaykumar0339</groupId>
    <artifactId>jexlang-java</artifactId>
    <version>1.0.0</version>
</dependency>
Enter fullscreen mode Exit fullscreen mode

Usage:

import io.github.vinaykumar0339.evaluator.JexEvaluator;

JexEvaluator jexlang = new JexEvaluator();

// Simple expression
Object result = jexlang.evaluate("1 + 2 * 3");  // 7

// With context
Map<String, Object> user = Map.of("name", "John", "age", 30);
Map<String, Object> context = Map.of("user", user);
Boolean isAdult = (Boolean) jexlang.evaluate("user.age >= 18", context);  // true
Enter fullscreen mode Exit fullscreen mode

πŸ†š How JexLang Compares

Feature JexLang Hardcoded Logic JSON Schema Other Expression Languages
Platform Agnostic βœ… Identical behavior ❌ Platform-specific ⚠️ Library-dependent ⚠️ Mostly single platform
Familiar Syntax βœ… JavaScript-like βœ… Native language ❌ Verbose JSON ⚠️ Varies (SpEL, OGNL, etc.)
Runtime Configurable βœ… ❌ βœ… βœ…
Complex Logic Support βœ… βœ… ❌ Limited βœ…
Learning Curve 🟒 Low (JS developers) 🟒 None 🟑 Medium 🟑 Medium to High
Consistent Cross-Platform βœ… Same AST/Interpreter ❌ ⚠️ Validator differences ❌ Usually single platform
Custom Functions βœ… βœ… ❌ βœ…
Transforms/Pipes βœ… value \ transform ⚠️ Manual ❌ ⚠️ Rare

πŸ—ΊοΈ Roadmap

Platform Status Use Cases
JavaScript/TypeScript βœ… Production Ready Browser apps, Node.js APIs, no-code/low-code rule engines
Java βœ… Production Ready Android apps, backend services, enterprise rule engines
Swift 🚧 In Progress iOS apps, macOS automation, workflow logic

Future considerations:

  • Python support

πŸ™ Call to Action

JexLang is open source and in its early days. I've built it to solve real problems I've faced, but I know the community will find use cases I never imagined.

Here's how you can help:

  1. ⭐ Star the repository β€” It helps with visibility and motivates continued development.

  2. πŸ§ͺ Try it in your projects β€” Put it through real-world scenarios and see how it holds up.

  3. πŸ› Report issues β€” Found a bug? Edge case that doesn't work? Let me know!

  4. πŸ’‘ Suggest features β€” What would make JexLang more useful for your use case?

  5. πŸ“’ Share with your network β€” Know someone who might benefit? Spread the word!


πŸ“š Resources


🎬 Final Thoughts

Cross-platform development is hard enough without having to maintain identical logic in multiple languages. JexLang aims to eliminate that pain point by providing a single, consistent way to express logic that works everywhere.

Whether you're building form validations, business rule engines, workflow automation, or config-driven applications, JexLang gives you the power to write once and run everywhere.

I'm excited to see what you build with it.


Have questions or feedback? Drop a comment below or reach out on GitHub. I'd love to hear from you!

#OpenSource #JavaScript #TypeScript #Java #Swift #ExpressionLanguage #FormValidation #CrossPlatform #DeveloperTools #Programming

Top comments (0)