feat: 添加对数组和对象字面量的尾随逗号支持,更新相关测试用例
This commit is contained in:
@@ -0,0 +1,182 @@
|
|||||||
|
// ============================================================================
|
||||||
|
// 实用脚本集合:文本统计、数组工具、排序、数据转换
|
||||||
|
// 运行: cargo run -- examples/practical.ast
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// 1. 文本统计工具
|
||||||
|
// ============================================================================
|
||||||
|
fn count_chars(s) {
|
||||||
|
return len(s)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn count_words(s) {
|
||||||
|
let count = 0
|
||||||
|
let in_word = false
|
||||||
|
for (c in s) {
|
||||||
|
if (c == " " || c == "\n" || c == "\t") {
|
||||||
|
in_word = false
|
||||||
|
} else if (!in_word) {
|
||||||
|
count = count + 1
|
||||||
|
in_word = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return count
|
||||||
|
}
|
||||||
|
|
||||||
|
fn text_stats(text) {
|
||||||
|
print("=== Text Stats ===")
|
||||||
|
print("chars:", count_chars(text))
|
||||||
|
print("words:", count_words(text))
|
||||||
|
}
|
||||||
|
|
||||||
|
let sample = "hello world from Aster scripting language"
|
||||||
|
text_stats(sample)
|
||||||
|
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// 2. 数组工具函数 (map / filter / reduce)
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
|
fn map(arr, func) {
|
||||||
|
let result = []
|
||||||
|
for (x in arr) {
|
||||||
|
push(result, func(x))
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
fn filter(arr, pred) {
|
||||||
|
let result = []
|
||||||
|
for (x in arr) {
|
||||||
|
if (pred(x)) {
|
||||||
|
push(result, x)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
fn reduce(arr, func, init) {
|
||||||
|
let acc = init
|
||||||
|
for (x in arr) {
|
||||||
|
acc = func(acc, x)
|
||||||
|
}
|
||||||
|
return acc
|
||||||
|
}
|
||||||
|
|
||||||
|
let nums = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
|
||||||
|
|
||||||
|
let doubled = map(nums, fn(x) { return x * 2 })
|
||||||
|
print("map (*2):", doubled)
|
||||||
|
|
||||||
|
let evens = filter(nums, fn(x) { return x % 2 == 0 })
|
||||||
|
print("filter (even):", evens)
|
||||||
|
|
||||||
|
let sum = reduce(nums, fn(a, b) { return a + b }, 0)
|
||||||
|
print("reduce (sum):", sum)
|
||||||
|
|
||||||
|
// 链式: 偶数平方和
|
||||||
|
let even_squares = map(
|
||||||
|
filter(nums, fn(x) { return x % 2 == 0 }),
|
||||||
|
fn(x) { return x * x }
|
||||||
|
)
|
||||||
|
let sum_squares = reduce(even_squares, fn(a, b) { return a + b }, 0)
|
||||||
|
print("sum of even squares:", sum_squares)
|
||||||
|
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// 3. 冒泡排序
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
|
fn sort(arr) {
|
||||||
|
let n = len(arr)
|
||||||
|
let i = 0
|
||||||
|
while (i < n - 1) {
|
||||||
|
let j = 0
|
||||||
|
while (j < n - i - 1) {
|
||||||
|
if (arr[j] > arr[j + 1]) {
|
||||||
|
let tmp = arr[j]
|
||||||
|
arr[j] = arr[j + 1]
|
||||||
|
arr[j + 1] = tmp
|
||||||
|
}
|
||||||
|
j = j + 1
|
||||||
|
}
|
||||||
|
i = i + 1
|
||||||
|
}
|
||||||
|
return arr
|
||||||
|
}
|
||||||
|
|
||||||
|
let unsorted = [42, 17, 8, 99, 3, 56]
|
||||||
|
print("unsorted:", unsorted)
|
||||||
|
print("sorted:", sort(unsorted))
|
||||||
|
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// 4. 数据转换 (类 JSON 处理)
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
|
let users = [
|
||||||
|
{ name: "Alice", age: 28, active: true },
|
||||||
|
{ name: "Bob", age: 35, active: false },
|
||||||
|
{ name: "Carol", age: 22, active: true },
|
||||||
|
{ name: "Dave", age: 40, active: true },
|
||||||
|
]
|
||||||
|
|
||||||
|
// 找出活跃用户的名字,按年龄排序
|
||||||
|
let active = filter(users, fn(u) { return u["active"] })
|
||||||
|
print("active users:", len(active))
|
||||||
|
|
||||||
|
// 计算平均年龄
|
||||||
|
let total_age = reduce(users, fn(acc, u) { return acc + u["age"] }, 0)
|
||||||
|
print("avg age:", total_age / len(users))
|
||||||
|
|
||||||
|
// 生成名字列表
|
||||||
|
let names = map(users, fn(u) { return u["name"] })
|
||||||
|
print("names:", names)
|
||||||
|
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// 5. 斐波那契 (递归 + 迭代 + 缓存对比)
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
|
fn fib_rec(n) {
|
||||||
|
if (n <= 1) { return n }
|
||||||
|
return fib_rec(n - 1) + fib_rec(n - 2)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn fib_iter(n) {
|
||||||
|
if (n <= 1) { return n }
|
||||||
|
let a = 0
|
||||||
|
let b = 1
|
||||||
|
let i = 2
|
||||||
|
while (i <= n) {
|
||||||
|
let tmp = a + b
|
||||||
|
a = b
|
||||||
|
b = tmp
|
||||||
|
i = i + 1
|
||||||
|
}
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
|
||||||
|
print("fib_rec(20):", fib_rec(20))
|
||||||
|
print("fib_iter(20):", fib_iter(20))
|
||||||
|
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// 6. 回文检测
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
|
fn is_palindrome(s) {
|
||||||
|
let n = len(s)
|
||||||
|
let i = 0
|
||||||
|
while (i < n / 2) {
|
||||||
|
if (s[i] != s[n - 1 - i]) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
i = i + 1
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
print("is_palindrome('radar'):", is_palindrome("radar"))
|
||||||
|
print("is_palindrome('hello'):", is_palindrome("hello"))
|
||||||
@@ -567,6 +567,10 @@ impl Parser {
|
|||||||
if !self.match_kind(&[TokenKind::Comma]) {
|
if !self.match_kind(&[TokenKind::Comma]) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
// Allow trailing comma
|
||||||
|
if self.check(&TokenKind::RightBrace) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
self.consume(TokenKind::RightBrace, "Expected '}' after object literal")?;
|
self.consume(TokenKind::RightBrace, "Expected '}' after object literal")?;
|
||||||
@@ -581,6 +585,10 @@ impl Parser {
|
|||||||
if !self.match_kind(&[TokenKind::Comma]) {
|
if !self.match_kind(&[TokenKind::Comma]) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
// Allow trailing comma
|
||||||
|
if self.check(&TokenKind::RightBracket) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
self.consume(TokenKind::RightBracket, "Expected ']' after array literal")?;
|
self.consume(TokenKind::RightBracket, "Expected ']' after array literal")?;
|
||||||
|
|||||||
@@ -551,6 +551,22 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn parse_array_trailing_comma() {
|
||||||
|
match parse_expr("[1, 2, 3,]") {
|
||||||
|
Expr::ArrayLiteral { elements } => assert_eq!(elements.len(), 3),
|
||||||
|
e => panic!("Expected ArrayLiteral, got {:?}", e),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn parse_object_trailing_comma() {
|
||||||
|
match parse_expr("{a: 1, b: 2,}") {
|
||||||
|
Expr::ObjectLiteral { properties } => assert_eq!(properties.len(), 2),
|
||||||
|
e => panic!("Expected ObjectLiteral, got {:?}", e),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// ----- statements -----
|
// ----- statements -----
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|||||||
Reference in New Issue
Block a user