mcpField
B
boxlang-language-fundamentals

boxlang-language-fundamentals

github.com/ortus-boxlang开发044 次安装1 周前更新
代码仓库

关于 boxlang-language-fundamentals

boxlang-language-fundamentals 提供 BoxLang 语言基础参考,面向编写或阅读 BoxLang 程序的开发者与 Agent。内容涵盖 JVM 语言特性、语法、类型、作用域及常见陷阱,并给出代码示例。

主要功能

  1. 01BoxLang 文件类型与用途
  2. 02变量、作用域与动态类型
  3. 03控制流与异常处理
  4. 04数组索引和 Java 可互操作性
  5. 05函数语法与内置函数

适用场景

  1. 01查阅 BoxLang 语法用法
  2. 02编写 BoxLang 脚本与类
  3. 03检查数组和作用域行为
  4. 04避免 Java 调用与分号常见错误

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/ortus-boxlang/skills --skill boxlang-language-fundamentals

安装后在 Claude Code 中输入/boxlang-language-fundamentals确认已加载。

详细介绍

BoxLang 语言基础

BoxLang 是面向 AI 的软件生产力平台,用于构建、现代化改造和运行应用程序,让开发者与 AI Agent 协同工作。

概述

BoxLang 是一种现代的动态 JVM 语言(JRE 21+),受到 Java、CFML、Python、 Ruby、Go 和 PHP 的影响。它会编译为 Java 字节码,并以更简洁、更具表现力的语法 提供完整的 Java 互操作能力。

文件类型

扩展名用途
.bx类文件(组件、服务、模型)
.bxs脚本文件(独立脚本、命令行工具)
.bxm标记/模板文件(HTML 输出、视图)

变量和作用域

BoxLang 使用动态类型——类型在运行时推断。可以选择添加显式类型 注解,但并非必需。

// Variable declaration (var keyword in functions)
var name = "BoxLang"
var count = 42
var price = 9.99
var active = true

// Null
var nothing = null
作用域链(函数中的解析顺序)
  1. local — 函数内用 var 声明的变量
  2. arguments — 函数参数
  3. variables — 组件/类的私有作用域
  4. this — 组件/类的公共作用域
  5. 命名作用域:url、form、session、application、request、cgi、server
// Explicit scope reference
variables.counter = 0
this.publicValue = "visible"
local.tempResult = compute()

运算符

// Arithmetic
a + b   a - b   a * b   a / b   a % b   a ^ b   a \ b  // integer divide

// Comparison
a == b   a != b   a > b   a < b   a >= b   a <= b
a === b  a !== b  // strict (type + value)

// Logical
a && b   a || b   !a
a and b  a or b   not a   // word operators

// String concatenation
a & b

// Safe-navigation (null-safe member access)
user?.address?.city   // returns null instead of throwing

// Inclusive range (v1.12+)
1..5   // produces [1, 2, 3, 4, 5] — both ends included

// Ternary
result = condition ? trueValue : falseValue

// Elvis (null coalescing)
result = value ?: "default"

控制流

// if / else if / else
if ( age >= 18 ) {
    writeOutput( "adult" )
} else if ( age >= 13 ) {
    writeOutput( "teen" )
} else {
    writeOutput( "child" )
}

// switch
switch ( status ) {
    case "active":
        doActive()
        break
    case "pending":
    case "review":
        doPending()
        break
    default:
        doDefault()
}

// for (index)
for ( var i = 1; i <= 10; i++ ) {
    writeOutput( i )
}

// for-in (collection)
for ( var item in myArray ) {
    writeOutput( item )
}

// for-in with destructuring (v1.12+)
for ( var [key, value] in myStruct ) {
    writeOutput( "#key# = #value#" )
}

// while
while ( queue.len() > 0 ) {
    process( queue.dequeue() )
}

// do-while
do {
    attempt = tryConnect()
} while ( !attempt.success && retries++ < 3 )

异常处理

try {
    result = riskyOperation()
} catch ( "CustomException" e ) {
    handleCustom( e )
} catch ( any e ) {
    // e.message, e.detail, e.stackTrace, e.type
    logError( e.message )
} finally {
    cleanup()
}

// Throwing exceptions
throw( message="Something went wrong", type="MyApp.ValidationError", detail="Field X is required" )

// Or as an object
throw new MyException( "Bad input" )

字符串

// Double-quoted: interpolation enabled
var greeting = "Hello, #name#!"
var multi = "Line one
Line two"

// Single-quoted: literal (no interpolation)
var literal = 'Hello, #name#'   // outputs literally: Hello, #name#

// Common string functions
len( str )
trim( str )
uCase( str ) / lCase( str )
left( str, n ) / right( str, n ) / mid( str, start, n )
replace( str, search, replacement )
reFind( pattern, str )
listToArray( str, delimiter )
str.contains( "foo" )   // member function syntax

数组

重要:BoxLang 数组的索引从 1 开始(而非像 Java 那样从 0 开始)。 第一个元素的索引始终为 1。

var fruits = [ "apple", "banana", "cherry" ]

// Access by index — starts at 1
fruits[ 1 ]   // "apple"  ✅
fruits[ 0 ]   // null / out-of-bounds  ❌  WRONG

// Preferred: use named accessors instead of numeric index
fruits.first()   // "apple"   — first element
fruits.last()    // "cherry"  — last element

// Common array operations
fruits.len()               // 3
fruits.append( "date" )    // adds to end
fruits.prepend( "avocado" ) // adds to front
fruits.isEmpty()           // false
fruits.contains( "banana" ) // true
fruits.find( "cherry" )    // 3 (1-based index, 0 if not found)
fruits.each( (f) -> println(f) )
fruits.map( (f) -> uCase(f) )
fruits.filter( (f) -> f.startsWith("a") )
fruits.reduce( (acc, f) -> acc & "," & f, "" )

// Inline array literal
var nums = [ 1, 2, 3, 4, 5 ]

// Spread (v1.12+)
var more = [ ...nums, 6, 7 ]
可变参数/位置参数 Java 调用必须使用数组

调用接受 Object... args(可变参数)的 Java 方法时,必须 传入 BoxLang 数组——直接传入单个值不起作用:

// WRONG — bare value
storage.query( "SELECT * FROM users WHERE id = ?", userId )

// CORRECT — wrapped in an array
storage.query( "SELECT * FROM users WHERE id = ?", [userId] )

// Multiple params
storage.query( "SELECT * FROM users WHERE role = ? AND active = ?", [role, active] )

分号

语句末尾的分号是可选的,且通常被认为是多余的。 不要在变量声明、函数调用、return 语句或控制流代码块中添加分号。

// BAD — noisy semicolons
var name = "BoxLang";
var total = items.len();
return total;

// GOOD — clean, no semicolons
var name = "BoxLang"
var total = items.len()
return total

以下两种情况需要(或通常会使用)分号:

  1. 自闭合组件标签 — 用于结束标签调用:

    bx:header name="Content-Type" value="application/json";
    bx:location url="/login" addToken=false;
    bx:abort;
    
  2. 类中的属性声明:

    class MyComponent {
        bx:property name="title" type="string" default="";
        bx:property name="count" type="numeric" default=0;
    }
    

类型系统

BoxLang 使用动态类型,并支持可选的类型强制:

// Type annotations (optional)
string function greet( required string name ) {
    return "Hello, #name#!"
}

// Auto-casting
var num = "42" + 0      // 42 (string auto-cast to number)
var bool = "true"       // truthy
var date = "2024-01-15" // auto-cast to date in date functions

// Type checking
isNumeric( val )
isDate( val )
isArray( val )
isStruct( val )
isNull( val )
getMetaData( obj ).name   // introspect type

现代特性(v1.12+)

// Array destructuring
var [first, second, ...rest] = myArray
var [a, b] = [1, 2]

// Struct destructuring
var { name, age } = person
var { name: fullName, age: years } = person  // rename

// Spread in function calls
var args = [1, 2, 3]
sum( ...args )

// Spread in array/struct literals
var combined = [...arr1, ...arr2]
var merged = {...struct1, ...struct2}

// For-loop destructuring
for ( var [key, val] in myStruct ) { ... }
for ( var [index, item] in myArray ) { ... }

注释

// Single-line comment

/* Multi-line
   comment */

/**
 * Doc-comment (used for annotation metadata)
 * @param name The user's name
 * @return Greeting string
 */

内置函数(BIF)

BIF 无需导入即可全局使用。可直接调用,也可作为成员函数调用:

// Function-style
len( myArray )
arrayAppend( myArray, item )
structKeyExists( myStruct, "key" )

// Member-function style (preferred)
myArray.len()
myArray.append( item )
myStruct.keyExists( "key" )

// List all available BIFs
writeDump( getFunctionList() )

输出

writeOutput( "Hello" )      // write to output buffer
println( "Hello" )          // write + newline (scripts)
dump( var=myVar )           // debug dump
writeDump( myVar )          // alias for dump
abort                       // stop execution

现代函数语法

不使用 function 关键字的方法声明

在 class 代码块中,function 关键字是可选的。BoxLang 支持简洁的声明风格, 并支持可选的冒号式返回类型注解:

class MathService {

    // Concise declaration — no "function" keyword
    add( numeric a, numeric b ) {
        return a + b
    }

    // With return-type annotation (colon syntax)
    multiply( required numeric a, required numeric b ):numeric {
        return a * b
    }

    // With default parameter value and return type
    power( required numeric base, numeric exponent = 2 ):numeric {
        return base ^ exponent
    }

    // Void-like — no return type declared
    logOperation( required string operation ) {
        writeLog( "Operation: #operation#" )
    }
}

可用的返回类型:string、numeric、boolean、array、struct、query、 date、void、any,或完全限定的类名。

传统的 function 关键字(仍然有效)
class Service {
    function calculate( required numeric value ):numeric {
        return value * 2
    }
}

两种风格等效。在类中优先使用简洁风格;对于独立的脚本级函数和闭包, 使用 function 关键字。

快速参考:常见问题

问题错误写法正确写法
数组索引arr[ 0 ]arr[ 1 ] 或 arr.first()
行末分号var x = 1;var x = 1
可变参数调用query( sql, singleVal )query( sql, [singleVal] )
在 BoxLang 中使用 CFML 函数cfheader( ... )bx:header name=... value=...;
不需要 Java 风格的 //不适用// 和 /* */ 均可使用

参考资料

评论

支持 Markdown