ChaiScript/grammar/chaiscript.ebnf
leftibot bb06919061
Fix #677: Add strong typedefs (#680)
* Fix #677: Add strong typedefs via 'using Type = BaseType' syntax

Strong typedefs create distinct types backed by Dynamic_Object, so
'using Meters = int' makes Meters a type that is not interchangeable
with int or other typedefs of int. The constructor Meters(val) wraps
the base value, and function dispatch enforces the type distinction.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* Address review: add to_underlying function for strong typedefs

Registers a to_underlying() function for each strong typedef that
returns the wrapped base value from the Dynamic_Object's __value attr.

Requested by @lefticus in PR #680 review.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* Address review: expose strongly-typed operators for strong typedefs

Register forwarding binary operators at typedef creation time via a
custom Proxy_Function_Base subclass (Strong_Typedef_Binary_Op). Each
operator unwraps __value from both operands, dispatches on the
underlying types, and re-wraps arithmetic results in the typedef.
Comparison operators return the raw bool.

- Arithmetic: +, -, *, /, % → Meters + Meters -> Meters
- Comparison: <, >, <=, >=, ==, != → Meters < Meters -> bool
- Operators that don't exist on the base type error at call time
  (e.g. StrongString * StrongString -> error)
- Users can extend typedefs with their own operations using
  to_underlying() for unwrapping

Tests cover int-based arithmetic, string-based concatenation, string
multiplication error, comparison ops, type safety of results, and
user-defined operator extensions.

Requested by @lefticus in PR #680 review.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* Address review: conditionally register operators based on underlying type support

Only register strong typedef operators that actually exist for the
underlying type. Previously all operators were added unconditionally,
causing confusing reflection entries (e.g. * for StrongString) that
would fail at runtime. Now each operator is probed via call_match
against default-constructed base type values before registration.

Also adds bitwise/shift operators (&, |, ^, <<, >>) for types that
support them, and expands test coverage for unsupported operator
rejection.

Requested by @lefticus in PR #680 review.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* Address review: register all operators unconditionally and add compound assignment operators

Remove conditional operator registration (op_exists_for_base_type check)
since users could add underlying operators later, and the runtime check was
expensive. Operators that fail on the underlying type now error at call time
instead of being absent. Add compound assignment operators (*=, +=, -=, /=,
%=, <<=, >>=, &=, |=, ^=) via Strong_Typedef_Compound_Assign_Op which
computes the base operation and stores the result back in __value.

Requested by @lefticus in PR #680 review.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* Merge upstream/develop into fix/issue-677-add-strong-typedefs

Resolve merge conflicts with ChaiScript:develop. Upstream added
nested namespace support (#675), grammar railroad diagrams (#673),
and WASM exception support (#689). Conflicts in chaiscript_common.hpp,
chaiscript_eval.hpp, and chaiscript_parser.hpp resolved by keeping
both Using and Namespace_Block AST node types.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* Address review: add strong typedef documentation to cheatsheet

Add a new "Strong Typedefs" section to the cheatsheet covering:
- Basic usage with `using Type = BaseType` syntax
- Arithmetic and comparison operator forwarding
- String-based strong typedefs
- Accessing the underlying value via to_underlying
- Extending strong typedefs with custom operations

Requested by @lefticus in PR #680 review.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: leftibot <leftibot@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-15 14:48:49 -06:00

190 lines
6.2 KiB
EBNF

/*
* ChaiScript Grammar EBNF for Railroad Diagram Generation
*
* View as navigable railroad diagrams at:
* https://www.bottlecaps.de/rr/ui (IPv6)
* https://rr.red-dove.com/ui (IPv4)
*
* Copy and paste this file into the 'Edit Grammar' tab, then
* click 'View Diagram'.
*
* This grammar uses the notation accepted by
* https://github.com/GuntherRademacher/rr :
* - "::=" as rule separator
* - no semicolon at end of rule
* - "?" "+" "*" for repetition
* - C comments
*/
/* ---- Top-level ---- */
statements ::= ( def | try | if | while | class | enum
| for | switch | return | break | continue
| equation | block | eol )+
/* ---- Functions ---- */
def ::= "def" id ( "::" id )? "(" decl_arg_list ")" eol*
( ":" guard )? eol* block
lambda ::= "fun" ( "[" id_arg_list "]" )? "(" decl_arg_list ")" eol* block
guard ::= operator
/* ---- Exception handling ---- */
try ::= "try" eol* block catch* finally?
catch ::= "catch" ( "(" arg ")" )? eol* block
finally ::= "finally" eol* block
/* ---- Control flow ---- */
if ::= "if" "(" equation ( eol equation )? ")" eol* block
( "else" ( if | eol* block ) )*
while ::= "while" "(" operator ")" eol* block
for ::= "for" "(" ( for_guards | equation ":" equation ) ")" eol* block
for_guards ::= equation eol equation eol equation
switch ::= "switch" "(" operator ")" eol* "{" ( case | default )+ "}"
case ::= "case" "(" operator ")" eol* block
default ::= "default" eol* block
/* ---- Classes ---- */
class ::= "class" id ( ":" id )? eol* class_block
class_block ::= "{" class_statements* "}"
class_statements ::= def | var_decl | eol
/* ---- Enums ---- */
enum ::= "enum" ( "class" | "struct" ) id ( ":" underlying_type )?
"{" enum_entries? "}"
enum_entries ::= enum_entry ( "," enum_entry )*
enum_entry ::= id ( "=" integer )?
underlying_type ::= id
/* ---- Blocks & flow keywords ---- */
block ::= "{" statements* "}"
return ::= "return" operator?
break ::= "break"
continue ::= "continue"
/* ---- Line termination ---- */
eol ::= "\n" | "\r\n" | ";"
/* ---- Equations & operators ---- */
equation ::= operator ( ( "=" | ":=" | "+=" | "-=" | "*=" | "/="
| "%=" | "<<=" | ">>=" | "&=" | "^=" | "|=" )
equation )?
operator ::= prefix
| value
| operator binary_operator operator
| operator "?" operator ":" operator
prefix ::= ( "++" | "--" | "-" | "+" | "!" | "~" ) operator
binary_operator ::= "||" | "&&"
| "|" | "^" | "&"
| "==" | "!="
| "<" | "<=" | ">" | ">="
| "<<" | ">>"
| "+" | "-"
| "*" | "/" | "%"
/* ---- Values & access ---- */
value ::= var_decl | dot_fun_array | prefix
dot_fun_array ::= ( lambda | num | quoted_string
| single_quoted_string | raw_string
| paren_expression | inline_container
| id )
( fun_call | array_call | dot_access )*
fun_call ::= "(" arg_list ")"
array_call ::= "[" operator "]"
dot_access ::= "." id
/* ---- Variable declarations ---- */
var_decl ::= ( "auto" | "var" | "const" ) ( reference | id )
| "global" ( reference | id )
| "attr" id ( "::" id )?
reference ::= "&" id
/* ---- Parenthesised & inline containers ---- */
paren_expression ::= "(" operator ")"
inline_container ::= "[" container_arg_list "]"
container_arg_list ::= value_range
| map_pair ( "," map_pair )*
| operator ( "," operator )*
value_range ::= operator ".." operator
map_pair ::= operator ":" operator
/* ---- String literals ---- */
quoted_string ::= '"' ( char | escape | interpolation )* '"'
single_quoted_string ::= "'" ( char | escape ) "'"
raw_string ::= 'R"' delimiter? "(" char* ")" delimiter? '"'
delimiter ::= [a-zA-Z0-9_]+
interpolation ::= "${" equation "}"
/* ---- Escape sequences ---- */
escape ::= "\" ( "'" | '"' | "?" | "\" | "a" | "b"
| "f" | "n" | "r" | "t" | "v" | "$"
| "0"
| "x" hex_digit+
| "u" hex_digit hex_digit hex_digit hex_digit
| "U" hex_digit hex_digit hex_digit hex_digit
hex_digit hex_digit hex_digit hex_digit
| octal_digit+ )
/* ---- Argument lists ---- */
id_arg_list ::= id ( "," id )*
decl_arg_list ::= ( arg ( "," arg )* )?
arg_list ::= ( equation ( "," equation )* )?
arg ::= id id?
/* ---- Identifiers ---- */
id ::= ( [a-zA-Z_] [a-zA-Z0-9_]* )
| ( "`" [^`]+ "`" )
| "true" | "false"
| "Infinity" | "NaN"
| "_"
| "__LINE__" | "__FILE__" | "__FUNC__" | "__CLASS__"
/* ---- Numeric literals ---- */
num ::= hex | binary | float | integer
hex ::= "0" ( "x" | "X" ) [0-9a-fA-F]+ int_suffix*
binary ::= "0" ( "b" | "B" ) [01]+ int_suffix*
float ::= [0-9]+ "." [0-9]+ ( ( "e" | "E" ) ( "+" | "-" )? [0-9]+ )? float_suffix?
integer ::= [0-9]+ int_suffix*
int_suffix ::= "l" | "L" | "ll" | "LL" | "u" | "U"
float_suffix ::= "l" | "L" | "f" | "F"
/* ---- Character classes ---- */
octal_digit ::= [0-7]
hex_digit ::= [0-9a-fA-F]
char ::= [^"\]