Skip to content

Language Design and Domain-Specific Languages (DSLs)

Overview

Grapa's executable BNF system enables unprecedented power in language design and domain-specific language (DSL) creation. Unlike traditional programming languages that require complex parser generators or compiler toolchains, Grapa allows you to define new languages, syntax, and DSLs directly within the language itself.

Key Advantages

1. Runtime Language Evolution

  • Dynamic Grammar Modification: Add new syntax constructs at runtime
  • Immediate Availability: New language features are available instantly
  • No Compilation Step: No need for separate compilation or build processes
  • Incremental Development: Build languages piece by piece

2. Executable BNF Architecture

  • Beyond Traditional Parsing: Not just syntax definition, but executable code
  • Three-Phase Processing: Input → Compilation → Execution Tree → Runtime Evaluation
  • Lazy Evaluation: Execution trees are evaluated only when needed
  • Context Management: Sophisticated parameter binding and execution context

3. Seamless Integration

  • Leverage Existing Libraries: Use Grapa's comprehensive C++ libraries
  • Native Performance: Custom syntax runs at native speed
  • Error Handling: Built-in error recovery and debugging support
  • Performance Optimization: Grammar compilation caching and optimization

Use Case Scenarios

1. Configuration Management Systems

Problem: Complex applications need flexible configuration systems that can handle various formats and validation rules.

Solution: Create a configuration DSL that supports multiple formats, validation, and dynamic loading.

/* Configuration DSL using custom_command and custom_function as variables */
custom_command = rule config '{' <$config_entries> '}' {op(entries:$3){
    ("Loading configuration...").echo();
    config = {};

    i = 0;
    while (i < entries.len()) {
        entry = entries[i];
        apply_config_entry(entry, config);
        i += 1;
    };

    return config;
}};

custom_command = rule config $STR '=' <$config_value> {op(key:$2,value:$4){
    return {key: key, value: value, type: "setting"};
}};

custom_command = rule include $STR {op(file:$2){
    return {file: file, type: "include"};
}};

/* Usage */
app_config = op(parse)('config { 
    server_name = "myapp" | 
    port = 8080 | 
    debug = true | 
    include "database.conf" | 
    include "logging.conf" 
}')();

Benefits: - Declarative Configuration: Clear, readable configuration syntax - Validation: Built-in type checking and validation - Modularity: Include external configuration files - Dynamic Loading: Load configuration at runtime

2. Data Processing Pipelines

Problem: Complex ETL (Extract, Transform, Load) workflows require custom processing logic and data transformation rules.

Solution: Create a pipeline DSL that defines data processing workflows declaratively.

/* ETL Pipeline DSL using custom_command and custom_function as variables */
custom_command = rule pipeline '{' <$pipeline_steps> '}' {op(steps:$3){
    ("Starting ETL pipeline...").echo();
    result = null;

    i = 0;
    while (i < steps.len()) {
        step = steps[i];
        ("Executing step: " + step).echo();
        result = execute_pipeline_step(step, result);
        i += 1;
    };

    ("Pipeline completed").echo();
    return result;
}};

custom_command = rule extract $STR {op(source:$2){
    return {action: "extract", source: source};
}};

custom_command = rule transform $STR {op(transformation:$2){
    return {action: "transform", transformation: transformation};
}};

custom_command = rule load $STR {op(target:$2){
    return {action: "load", target: target};
}};

/* Usage */
result = op(parse)('pipeline { 
    extract "users.csv" | 
    transform "clean_data" | 
    transform "validate_emails" | 
    load "users.db" 
}')();

Benefits: - Declarative Workflows: Define data processing as readable pipelines - Reusability: Pipeline steps can be reused across different workflows - Monitoring: Built-in execution tracking and logging - Error Handling: Robust error recovery and fallback mechanisms

3. API Definition Languages

Problem: REST APIs and web services need clear, maintainable definitions that can generate documentation, client code, and server implementations.

Solution: Create an API definition DSL that describes endpoints, data models, and behavior.

/* API Definition DSL */
custom_command = rule api $STR '{' <$api_endpoints> '}' {op(name:$2,endpoints:$4){
    ("Defining API: " + name).echo();
    return create_api(name, endpoints);
}};

custom_command = rule $STR $STR '{' <$endpoint_body> '}' {op(method:$1,path:$2,body:$4){
    return define_endpoint(method, path, body);
}};

/* Usage */
user_api = op(parse)('api "user_management" { 
    GET "/users" { 
        return get_all_users(); 
    } | 
    POST "/users" { 
        return create_user(request.body); 
    } | 
    PUT "/users/{id}" { 
        return update_user(id, request.body); 
    } | 
    DELETE "/users/{id}" { 
        return delete_user(id); 
    } 
}')();

Benefits: - Clear Documentation: Self-documenting API definitions - Code Generation: Generate client libraries and server stubs - Validation: Automatic request/response validation - Testing: Generate test cases from API definitions

4. Protocol Parsing and Network Communication

Problem: Network protocols, data formats, and communication protocols require custom parsing and serialization logic.

Solution: Create protocol-specific DSLs that handle parsing, validation, and serialization.

/* HTTP Protocol DSL */
custom_function = rule http_parse '(' $STR ')' {op(request:$3){
    lines = request.split("\n");
    if (lines.len() > 0) {
        first_line = lines[1];
        parts = first_line.split(" ");
        if (parts.len() >= 3) {
            return {
                method: parts[1],
                path: parts[2],
                version: parts[3],
                headers: parse_headers(lines)
            };
        };
    };
    return $err("Invalid HTTP request");
}};

custom_function = rule http_build '(' <$http_response> ')' {op(response:$3){
    status_line = "HTTP/1.1 " + response.status + " " + response.message;
    headers = build_headers(response.headers);
    body = response.body ? response.body : "";
    return status_line + "\r\n" + headers + "\r\n" + body;
}};

/* Usage */
request = "GET /api/users HTTP/1.1\nHost: example.com\nContent-Type: application/json\n";
parsed = op(parse)('http_parse("' + request + '")')();

response = {
    status: 200,
    message: "OK",
    headers: {"Content-Type": "application/json"},
    body: '{"users": []}'
};
http_response = op(parse)('http_build(' + response + ')')();

Benefits: - Protocol-Specific: Tailored parsing for specific protocols - Validation: Built-in protocol validation and error handling - Bidirectional: Support for both parsing and serialization - Extensibility: Easy to extend for new protocol versions

5. Validation and Business Rules

Problem: Complex business logic and data validation rules need to be expressed clearly and maintained by non-programmers.

Solution: Create a validation DSL that allows business analysts to define validation rules.

/* Validation DSL */
custom_command = rule validate $STR '{' <$validation_rules> '}' {op(data:$2,rules:$4){
    ("Validating data: " + data).echo();
    validation_result = true;
    errors = [];

    i = 0;
    while (i < rules.len()) {
        rule = rules[i];
        rule_result = apply_validation_rule(data, rule);
        if (!rule_result.valid) {
            validation_result = false;
            errors += rule_result.error;
        };
        i += 1;
    };

    return {valid: validation_result, errors: errors};
}};

custom_command = rule field $STR required {op(field_name:$2){
    return {type: "required", field: field_name};
}};

custom_command = rule field $STR type $STR {op(field_name:$2,type_name:$4){
    return {type: "type_check", field: field_name, expected_type: type_name};
}};

custom_command = rule field $STR range '(' $INT ',' $INT ')' {op(field_name:$2,min:$5,max:$7){
    return {type: "range", field: field_name, min: min, max: max};
}};

/* Usage */
validation_result = op(parse)('validate user_data { 
    field "name" required | 
    field "age" type "integer" | 
    field "age" range(18, 65) | 
    field "email" type "email" 
}')();

Benefits: - Business-Friendly: Rules can be written by non-programmers - Declarative: Clear, readable validation logic - Composable: Rules can be combined and reused - Maintainable: Easy to modify and extend validation rules

Advanced Patterns

1. Recursive Grammar Rules

/* CSV Parser with Recursive Grammar */
$global["$csv_parser"] = rule <$csv_row> ('\n' <$csv_parser> | '\n' | ) {
    op(row:$1, rest:$3) {
        if (rest) {
            return [row] + rest;
        } else {
            return [row];
        };
    }
};

$global["$csv_row"] = rule <$csv_field> (',' <$csv_row> | );

/* Parse complex nested structures */
csv_data = "name,age,city\nJohn,25,NY\nJane,30,LA";
parsed = op(parse)(csv_data)();

2. Context-Sensitive Parsing

/* Context-Aware Parser */
$global["$context_parser"] = rule <$context_state> <$context_rule> {
    op(state:$1, rule:$2) {
        return parse_with_context(state, rule);
    }
};

$global["$context_state"] = rule 'in' $STR '{' | 'out' $STR '}';

/* Use context for different parsing modes */
sql_result = op(parse)('in "sql" { SELECT * FROM users }')();
json_result = op(parse)('in "json" { {"name": "John"} }')();

3. Dynamic Token Types

/* Custom Token Types */
$global["$custom_token"] = rule $SYM("SQL_KEYWORD") | $SYM("JSON_PATH") | $SYM("XPATH_EXPR");

/* Use custom tokens in grammar */
$global["$sql_statement"] = rule $SYM("SQL_KEYWORD") <$sql_expression> {
    op(keyword:$1, expr:$2) {
        return execute_sql_statement(keyword, expr);
    }
};

Performance Optimization

1. Grammar Compilation Caching

/* Cache compiled grammar rules */
grammar_cache = {};

compile_grammar = op(rule_name, rule_def) {
    if (grammar_cache[rule_name]) {
        return grammar_cache[rule_name];
    };

    compiled = op(parse)(rule_def);
    grammar_cache[rule_name] = compiled;
    return compiled;
};

/* Reuse compiled grammars */
sql_grammar = compile_grammar("sql", "custom_command = rule select...");
json_grammar = compile_grammar("json", "custom_function = rule $STR->$STR...");

2. Lazy Evaluation

/* Lazy evaluation for expensive operations */
$global["$lazy_expression"] = rule 'lazy' '{' <$expression> '}' {
    op(expr:$3) {
        return op() {
            return expr();
        };
    }
};

/* Defer expensive calculations */
lazy_result = op(parse)('lazy { expensive_calculation() }')();
/* Only executed when lazy_result() is called */

Best Practices

1. Grammar Design

  1. Start Simple: Begin with basic rules and gradually add complexity
  2. Use Clear Names: Name rules and tokens descriptively
  3. Handle Errors: Include error recovery and fallback mechanisms
  4. Document Grammar: Provide clear documentation for custom syntax

2. Performance

  1. Cache Compiled Rules: Reuse compiled grammar rules when possible
  2. Optimize Token Types: Use appropriate token types for efficiency
  3. Lazy Evaluation: Defer expensive operations until needed
  4. Profile Execution: Monitor performance of custom grammar rules

3. Integration

  1. Leverage Existing Libraries: Use Grapa's C++ libraries when possible
  2. Follow Patterns: Use established patterns like direct BNF integration for native features, custom_command/custom_function as variables that leverage existing grammar rules
  3. Test Thoroughly: Validate grammar rules with comprehensive testing
  4. Version Control: Track grammar changes and maintain compatibility

Real-World Applications

1. Financial Trading Systems

  • Order DSL: Define trading orders and strategies
  • Risk Management: Validation rules for position limits
  • Market Data: Protocol parsers for market feeds

2. Healthcare Systems

  • Medical Records: Validation DSL for patient data
  • Clinical Protocols: Workflow DSL for treatment plans
  • Compliance: Regulatory validation rules

3. IoT and Embedded Systems

  • Device Configuration: Configuration DSL for IoT devices
  • Protocol Parsing: Custom protocols for sensor data
  • Command Interfaces: Device control DSLs

4. Content Management

  • Template Languages: Custom template syntax
  • Content Validation: Business rules for content approval
  • Workflow DSL: Content publishing workflows

Conclusion

Grapa's executable BNF system provides unprecedented power for language design and DSL creation. By leveraging this system, you can:

  • Create custom languages tailored to specific domains
  • Build sophisticated DSLs for complex workflows
  • Extend Grapa itself with new syntax and capabilities
  • Implement protocol parsers for various data formats
  • Design configuration languages for dynamic systems

The key is to start with simple patterns and gradually build complexity, always keeping in mind the three-phase processing model and the distinction between commands and functions.

See Also