mirror of
https://github.com/ChaiScript/ChaiScript.git
synced 2026-04-30 19:09:26 +08:00
* Fix #628: Add EBNF grammar for railroad diagram generation Add a formal EBNF grammar file (grammar/chaiscript.ebnf) that can be pasted into rr (https://www.bottlecaps.de/rr/ui) to produce navigable railroad diagrams of ChaiScript's syntax. The grammar was validated against the parser implementation and covers all language constructs including class inheritance, guard conditions, raw strings, and const declarations that were missing from the original proposal. A reference section was added to the cheatsheet, and a regression test exercises every documented grammar construct. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Address review: add grammar railroad diagram link to README Add a Grammar section to readme.md linking to the EBNF grammar file and to mingodad's railroad diagram generator for direct viewing. Requested by @lefticus in PR #673 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>
220 lines
4.4 KiB
ChaiScript
220 lines
4.4 KiB
ChaiScript
// Regression test: exercises grammar constructs documented in grammar/chaiscript.ebnf
|
|
|
|
// --- Variable declarations ---
|
|
var a = 1
|
|
auto b = 2
|
|
global c = 3
|
|
const d = 42
|
|
|
|
assert_equal(1, a)
|
|
assert_equal(2, b)
|
|
assert_equal(3, c)
|
|
assert_equal(42, d)
|
|
|
|
// --- Reference variables ---
|
|
var orig = 10
|
|
var &ref = orig
|
|
ref = 20
|
|
assert_equal(20, orig)
|
|
|
|
// --- Numeric literals ---
|
|
assert_equal(255, 0xFF)
|
|
assert_equal(255, 0xff)
|
|
assert_equal(5, 0b101)
|
|
assert_equal(42, 42)
|
|
assert_equal(3.14, 3.14)
|
|
|
|
// --- String interpolation ---
|
|
var name = "world"
|
|
assert_equal("hello world", "hello ${name}")
|
|
|
|
// --- Escape sequences ---
|
|
assert_equal("\n", "\n")
|
|
assert_equal("\t", "\t")
|
|
|
|
// --- Single-quoted char ---
|
|
assert_equal('A', 'A')
|
|
|
|
// --- Operators and precedence ---
|
|
assert_equal(7, 1 + 2 * 3)
|
|
assert_equal(true, 5 > 3 && 2 < 4)
|
|
assert_equal(true, false || true)
|
|
assert_equal(6, 3 << 1)
|
|
assert_equal(1, 3 >> 1)
|
|
assert_equal(5, 7 & 5)
|
|
assert_equal(7, 5 | 3)
|
|
assert_equal(6, 5 ^ 3)
|
|
assert_equal(-1, ~0)
|
|
|
|
// --- Ternary operator ---
|
|
assert_equal("yes", true ? "yes" : "no")
|
|
assert_equal("no", false ? "yes" : "no")
|
|
|
|
// --- Prefix operators ---
|
|
var x = 5
|
|
++x
|
|
assert_equal(6, x)
|
|
--x
|
|
assert_equal(5, x)
|
|
assert_equal(true, !false)
|
|
|
|
// --- Assignment operators ---
|
|
var v = 10
|
|
v += 5; assert_equal(15, v)
|
|
v -= 3; assert_equal(12, v)
|
|
v *= 2; assert_equal(24, v)
|
|
v /= 4; assert_equal(6, v)
|
|
v %= 4; assert_equal(2, v)
|
|
v <<= 2; assert_equal(8, v)
|
|
v >>= 1; assert_equal(4, v)
|
|
v |= 3; assert_equal(7, v)
|
|
v &= 5; assert_equal(5, v)
|
|
v ^= 3; assert_equal(6, v)
|
|
|
|
// --- Lambda ---
|
|
var add = fun(a, b) { a + b }
|
|
assert_equal(5, add(2, 3))
|
|
|
|
// --- Lambda with capture ---
|
|
var captured = 100
|
|
var get_captured = fun[captured]() { captured }
|
|
assert_equal(100, get_captured())
|
|
|
|
// --- Function definition ---
|
|
def multiply(a, b) { a * b }
|
|
assert_equal(12, multiply(3, 4))
|
|
|
|
// --- Guard condition on function ---
|
|
def abs_val(x) : x >= 0 { x }
|
|
def abs_val(x) : x < 0 { -x }
|
|
assert_equal(5, abs_val(5))
|
|
assert_equal(5, abs_val(-5))
|
|
|
|
// --- Class definition ---
|
|
class Animal
|
|
{
|
|
attr sound
|
|
def Animal(s) { this.sound = s }
|
|
def speak() { this.sound }
|
|
}
|
|
|
|
var dog = Animal("woof")
|
|
assert_equal("woof", dog.speak())
|
|
|
|
// --- Class with inheritance ---
|
|
class Puppy : Animal
|
|
{
|
|
attr name
|
|
def Puppy(n, s) { this.name = n; this.sound = s }
|
|
def greet() { to_string(this.name) + " says " + to_string(this.speak()) }
|
|
}
|
|
|
|
var p = Puppy("Rex", "yip")
|
|
assert_equal("Rex says yip", p.greet())
|
|
|
|
// --- Control flow: if/else ---
|
|
var result = ""
|
|
if (true) { result = "yes" } else { result = "no" }
|
|
assert_equal("yes", result)
|
|
|
|
// --- Control flow: while ---
|
|
var counter = 0
|
|
while (counter < 3) { ++counter }
|
|
assert_equal(3, counter)
|
|
|
|
// --- Control flow: for ---
|
|
var sum = 0
|
|
for (var i = 0; i < 5; ++i) { sum += i }
|
|
assert_equal(10, sum)
|
|
|
|
// --- Control flow: ranged for ---
|
|
var items = [10, 20, 30]
|
|
var total = 0
|
|
for (item : items) { total += item }
|
|
assert_equal(60, total)
|
|
|
|
// --- Switch/case ---
|
|
def classify(n) {
|
|
var label = ""
|
|
switch (n) {
|
|
case (1) { label = "one"; break }
|
|
case (2) { label = "two"; break }
|
|
default { label = "other" }
|
|
}
|
|
return label
|
|
}
|
|
assert_equal("one", classify(1))
|
|
assert_equal("two", classify(2))
|
|
assert_equal("other", classify(99))
|
|
|
|
// --- Try/catch/finally ---
|
|
var caught = false
|
|
var finalized = false
|
|
try {
|
|
throw("oops")
|
|
} catch (e) {
|
|
caught = true
|
|
} finally {
|
|
finalized = true
|
|
}
|
|
assert_true(caught)
|
|
assert_true(finalized)
|
|
|
|
// --- Inline containers ---
|
|
var vec = [1, 2, 3]
|
|
assert_equal(3, vec.size())
|
|
|
|
var m = ["a": 1, "b": 2]
|
|
assert_equal(1, m["a"])
|
|
|
|
var r = [1, 2, 3, 4, 5]
|
|
assert_equal(5, r.size())
|
|
|
|
// --- Dot access chaining ---
|
|
assert_equal(3, [1, 2, 3].size())
|
|
|
|
// --- Array access ---
|
|
var arr = [10, 20, 30]
|
|
assert_equal(20, arr[1])
|
|
|
|
// --- Backtick identifier ---
|
|
var `my var` = 42
|
|
assert_equal(42, `my var`)
|
|
|
|
// --- Special identifiers ---
|
|
assert_equal(true, true)
|
|
assert_equal(false, false)
|
|
|
|
// --- Nested block ---
|
|
var block_result = 0
|
|
{ block_result = 42 }
|
|
assert_equal(42, block_result)
|
|
|
|
// --- Break and continue ---
|
|
var break_sum = 0
|
|
for (var i = 0; i < 10; ++i) {
|
|
if (i == 5) { break }
|
|
break_sum += i
|
|
}
|
|
assert_equal(10, break_sum)
|
|
|
|
var cont_sum = 0
|
|
for (var i = 0; i < 5; ++i) {
|
|
if (i == 2) { continue }
|
|
cont_sum += i
|
|
}
|
|
assert_equal(8, cont_sum)
|
|
|
|
// --- Return from function ---
|
|
def early_return(n) {
|
|
if (n > 0) { return "positive" }
|
|
return "non-positive"
|
|
}
|
|
assert_equal("positive", early_return(1))
|
|
assert_equal("non-positive", early_return(-1))
|
|
|
|
// --- Colon assignment ---
|
|
var ca = 0
|
|
ca := 99
|
|
assert_equal(99, ca)
|