CScript Domain-Specific Language

CScript, a domain-specific scripting language, designed specifically for JSchema document. This lightweight, interpreted, and dynamically typed C-style language seamlessly integrates with the foundational principles of the existing JSchema architecture. With the use of dynamic typing and programming constructs similar to both JSON and JavaScript, streamlines the process of writing concise, user-friendly, and flexible logic for constraint or validation functions.

Although CScript is similar to JavaScript in many aspects, the CScript interpreter is designed to quickly halt (fail-fast) and report errors instead of trying to continue with potentially corrupted or unexpected data. This ensures a seamless alignment with the validation requirements of modern web services, significantly enhancing readability and productivity in both documentation and implementation, as well as reducing the potential for bugs.

Keywords & Reserved Words

Here is a list of keywords (reserved words) in the CScript language. You cannot use any of the following words as identifiers in your scripts. Some keywords are reserved for future extensions and are not currently in use.

Description Current Keyword (Reserved Word)
Variable Declaration var; (const)
Conditional Flow Control if; else; (switch; case; default)
Iterative Flow Control for; foreach; while; (do)
Jump Control break; (continue)
Membership Management in; (not)
Function Declaration function; constraint; future; subroutine
Function Data & Control return; target; caller
Literals Value true; false; null; undefined
Exception Management tryof; throw
Class & Object Management (class; new; super; this)
Script Modularization (import)

Data Types

Data types play a pivotal role in specifying the fundamental structure of values that serve as the foundational components for data manipulation within CScript. In the dynamically typed CScript language, data types are classified into two main categories: primitive immutable value types and composite reference types.

Composite types include #array and #object, while the remaining types fall under the category of primitive types. Both primitive values and composite references of schema accessible from CScript are readonly and unmodifiable nodes. The following table lists CScript data types:

SN Type Name Example
1 #array [1, 2, 3]; ["item1", [2.5, 5.8]]; []
2 #boolean true; false
3 #double 10.5; 20E-5; 5E+5
4 #integer 10; 20; 100; 500
5 #null null
6 #object { k1: "text", k2: { "k11" : 10 } }; {}
7 #range 1..10; -10..-5; -10..; ..100
8 #string "any text"; ""
9 #undefined undefined
10 #void Used only for internal purposes

The #void type is reserved for internal operations, including initializing unassigned l-values (variables, properties, and array elements) to the default state, among other cases. To explicitly represent the absence of a value in your script, use the undefined or null literal.

Operators & Precedences

CScript operators are symbols that are used to perform operations on variables and values. The direct operation of any operator that requires a modifiable l-value including ++, -- or = will raise an exception for the readonly schema nodes. The following table lists the operators according to their precedences from the highest to the lowest:

SN Category Operator
1 Property Access & Parentheses .; []; ()
2 Unary Plus/Minus & Logical Not +; -; !
3 Postfix Increment/Decrement i++; i--
4 Prefix Increment/Decrement ++i; --i
5 Arithmetic Multiplicative *; /; %
6 Arithmetic Additive +; -
7 Sequence Range ..
8 Relational Comparison >; <; >=; <=
9 Equality Comparison ==; !=
10 Logical And (Short-Circuit) &&
11 Logical Or (Short-Circuit) ||
12 Assignment (Augmented) =; +=; -=; *=; /=; %=

Function Types

Function types are essential for specifying the executable units that serve as the building-blocks of validation process within CScript. All function types can also accept variable number of arguments, specified by an ellipsis ... after the last parameter name which is then bound to an array containing the remaining arguments.

Additionally, all types of functions can be overloaded with varying numbers of fixed parameters, along with one that includes a variable argument parameter. A function name can be overloaded with only one variable argument definition, regardless of the number of required arguments preceding it. Fixed argument functions always take precedence or priority over variable argument functions when arguments match both definitions. Below are the various kinds of functions, each with distinct purposes, used in the CScript language:

Constraint Function

The constraint function defines conditions for the target JSON value. They assess whether the target JSON value satisfies the conditions specified by the functions. The function should return true if the conditions are met; otherwise, it should return false. Even if the function exits without returning any value, it is assumed that all conditions are met since any early return from the function usually implies that the conditions are not satisfied.

Within the scope including nested scopes of a constraint function, the target keyword refers to the JSON value or node to which the constraint function is applied, and the caller keyword refers to the schema node that invokes this constraint function. The subsequent example illustrates various alternative forms of the definition of a constraint function named example with the main keyword constraint:

constraint example(param1, param2, param3) {  }
constraint function example(param1, param2, params...) {  }

Future Constraint Function

The future constraint function extends the utility of the constraint function by also considering receiver values. They ensure that the validations are deferred until the receivers of the schema have received their anticipated values, thus evaluating the specified conditions imposed by the functions at a delayed phase. The subsequent example illustrates various alternative forms of the definition of a future constraint function named example with the main keyword future:

future example(param1, param2, param3) {  }
future constraint example(param1, param2, param3) {  }
future function example(param1, param2, param3) {  }
future constraint function example(param1, param2, params...) {  }

Subroutine Function

The subroutine function supports the constraint function by promoting code reusability, readability, and modularization. These functions serve as auxiliary units, enhancing the organization and maintainability of validation in CScript, and are not available in the schema context for invocation. Conversely, the constraint function, as well as the future function, are special functions available in the schema context, and are not invocable from the script context without the target and caller information, thereby preventing any potential overloading conflicts between subroutine and constraint functions. The subsequent example illustrates various alternative forms of the definition of a subroutine function named example with the main keyword subroutine:

subroutine example(param1, param2, param3) {  }
subroutine function example(param1, param2, params...) {  }

The CScript language provides a wide range of functions, data types, and programming constructs that can be used to implement diverse validation logic. This allows for the handling of complex validation requirements, both at the level of individual JSON values and groups of values received across different parts of the JSON document, ultimately ensuring the structural integrity of the entire JSON document.