Skip to content

DealModel Grammar (ANTLR4)

INFO

This is the complete ANTLR4 grammar for the current DealModel DSL. It uses lowercase keywords, treats whitespace as insignificant, supports line/block comments, and encodes the primitives: model header, variable, operator, event, clause, block, and type with clause composition and conditionals (when).

Save the code block below as DealModel.g4.

antlr
// DealModel.g4 — stable grammar with clause kinds (simple, guarantee, contingent) and intrinsic
// function call fix for `amount(...)`


grammar DealModel;

// ========================= PARSER RULES =========================

model: modelHeader statement* EOF;

modelHeader:
	MODEL LBRACE DEALTYPE COLON (TEXT | STRING) COMMA VERSION COLON SEMVER RBRACE;

statement:
	inputsBlock
	| overridesBlock
	| selectorsBlock
	| computationsBlock
	| variableDecl
	| operatorDef
	| eventDef
	| clauseDef
	| blockDef
	| typeDef;

inputsBlock:
	INPUTS LBRACE (
		SCHEMA COLON (TRIPLE_STRING | STRING)
		| REF COLON STRING
	) RBRACE;

overridesBlock:
	OVERRIDES LBRACE TEMPLATES COLON TRIPLE_STRING RBRACE;

selectorsBlock: SELECTORS LBRACE selectorEntry+ RBRACE;

selectorEntry: ID COLON (STRING | TEXT) SEMI?;

computationsBlock:
	COMPUTATIONS LBRACE (metricDef | outputDef)+ RBRACE;

metricDef: METRIC ID ASSIGN expr SEMI?;

outputDef: OUTPUT ID ASSIGN expr SEMI?;

variableDecl: VAR ID (ASSIGN expr)? SEMI?;

// Arithmetic / functional expression
expr:
	SUB expr				# unaryMinus
	| expr MUL expr			# mul
	| expr DIV expr			# div
	| expr ADD expr			# add
	| expr SUB expr			# sub
	| funcCall				# call
	| LPAREN expr RPAREN	# group
	| NUMBER				# num
	| ID					# name;

funcCall: (ID | AMOUNTKW) LPAREN (argList)? RPAREN;

argList: expr (COMMA expr)*;

// Boolean expressions (events)
boolExpr:
	boolExpr ANDAND boolExpr
	| boolExpr OROR boolExpr
	| NOT boolExpr
	| TRUE
	| FALSE
	| expr CMP expr
	| LPAREN boolExpr RPAREN;

// Operators
operatorDef:
	OPERATOR LBRACE NAME COLON ID DISPLAY COLON (TEXT | STRING) TYPEKW COLON operatorType RBRACE;

operatorType: AND | OR;

// Events
eventDef:
	EVENT LBRACE NAME COLON ID DESCRIPTION COLON (TEXT | STRING) CONDITION COLON boolExpr RBRACE;

// Clauses with kind, amount, payable
clauseDef:
	CLAUSE LBRACE NAME COLON ID KIND COLON clauseKind DESCRIPTION COLON (
		TEXT
		| STRING
	) (WHEN COLON ID)? (AMOUNTKW COLON expr)? (
		PAYABLE COLON payableSpec
	)? (TEMPLATE COLON TRIPLE_STRING)? RBRACE;

clauseKind: SIMPLE | GUARANTEE | CONTINGENT;

payableSpec: ON ID | BY_SCHEDULE ID;

// Clause Blocks
blockDef:
	BLOCK LBRACE NAME COLON ID EXPR COLON clauseExpr (
		WHEN COLON ID
	)? (TEMPLATE COLON TRIPLE_STRING)? RBRACE;

clauseExpr: clauseTerm (ID clauseTerm)*;

clauseTerm: ID | LPAREN clauseExpr RPAREN;

// Deal Type
typeDef:
	TYPEKW LBRACE NAME COLON ID COMPOSE COLON clauseExpr (
		TEMPLATE COLON TRIPLE_STRING
	)? RBRACE;

// ========================= LEXER RULES =========================

// Header keywords
MODEL: 'model';
DEALTYPE: 'deal_type';
VERSION: 'version';

// Primitives
INPUTS: 'inputs';
OVERRIDES: 'overrides';
SELECTORS: 'selectors';
COMPUTATIONS: 'computations';
VAR: 'var';
OPERATOR: 'operator';
EVENT: 'event';
CLAUSE: 'clause';
BLOCK: 'block';
TYPEKW: 'type';

// Fields
SCHEMA: 'schema';
REF: 'ref';
TEMPLATES: 'templates';
NAME: 'name';
DISPLAY: 'display';
DESCRIPTION: 'description';
KIND: 'kind';
WHEN: 'when';
AMOUNTKW: 'amount';
PAYABLE: 'payable';
ON: 'on';
BY_SCHEDULE: 'by_schedule';
CONDITION: 'condition';
EXPR: 'expr';
COMPOSE: 'compose';
TEMPLATE: 'template';
IN: 'in';
AND: 'and';
OR: 'or';

// Clause kinds
SIMPLE: 'simple';
GUARANTEE: 'guarantee';
CONTINGENT: 'contingent';

// Computations
METRIC: 'metric';
OUTPUT: 'output';

// Logical
ANDAND: '&&';
OROR: '||';
NOT: '!';
TRUE: 'true';
FALSE: 'false';

// Punctuation
LBRACE: '{';
RBRACE: '}';
COLON: ':';
COMMA: ',';
SEMI: ';';
ASSIGN: '=';
LPAREN: '(';
RPAREN: ')';

// Operators
ADD: '+';
SUB: '-';
MUL: '*';
DIV: '/';
CMP: '==' | '!=' | '>=' | '<=' | '>' | '<';

// Tokens
ID: [a-zA-Z_][a-zA-Z_0-9]*;

// Semantic version
SEMVER: INT '.' INT '.' INT ( '-' PRERELEASE)? ( '+' BUILD)?;
fragment INT: '0' | [1-9][0-9]*;
fragment PRERELEASE: IDENT ('.' IDENT)*;
fragment BUILD: IDENT ('.' IDENT)*;
fragment IDENT: [0-9A-Za-z-]+;

// Numbers, currency
NUMBER: [0-9]+ ('.' [0-9]+)?;
ISO_CCY: [A-Z]{3};

// Dates
DATE: [0-9]{4} '-' [0-9]{2} '-' [0-9]{2};

// Strings
TRIPLE_STRING: '"""' ( . | '\r' | '\n')*? '"""';
STRING: '"' ( '\\' . | ~["\\\r\n])* '"';
TEXT: (
		~[{}(),:="\r\n \u00A0\u1680\u2000-\u200A\u202F\u205F\u3000]
	)+;

// Whitespace & Comments (Unicode-safe)
fragment UNI_WS_CHAR:
	'\u0009'
	| '\u000B'
	| '\u000C'
	| '\u0020'
	| '\u00A0'
	| '\u1680'
	| '\u2000' ..'\u200A'
	| '\u202F'
	| '\u205F'
	| '\u3000';

WS: (UNI_WS_CHAR | '\r' | '\n')+ -> skip;
HASH_COMMENT: '#' ~[\r\n]* -> skip;
LINE_COMMENT: '//' ~[\r\n]* -> skip;
BLOCK_COMMENT: '/*' .*? '*/' -> skip;

Notes

  • Lowercase keywords are required (e.g., model, event, block).
  • Whitespace is ignored by the lexer (WS rule), so formatting is flexible.
  • Use events with when: to guard clauses/blocks; use operators inside expr: to compose logic.
  • The same TYPEKW: 'type' token names the exported composition; it is also used as a field key where applicable.
  • Extend TEXT or add quoted-string rules if you need commas or braces inside descriptions.

Confidential. For internal use only.