Compare commits
2 Commits
a3e385480d
...
dfe2a85d69
| Author | SHA1 | Date |
|---|---|---|
|
|
dfe2a85d69 | |
|
|
965a061463 |
|
|
@ -65,6 +65,7 @@ impl BytecodeGenerator {
|
||||||
crate::parser::Type::U32 => 0x02,
|
crate::parser::Type::U32 => 0x02,
|
||||||
crate::parser::Type::F64 => 0x03,
|
crate::parser::Type::F64 => 0x03,
|
||||||
crate::parser::Type::String => 0x04,
|
crate::parser::Type::String => 0x04,
|
||||||
|
crate::parser::Type::U128 => 0x05,
|
||||||
};
|
};
|
||||||
self.emit_byte(type_byte);
|
self.emit_byte(type_byte);
|
||||||
}
|
}
|
||||||
|
|
@ -117,7 +118,7 @@ impl BytecodeGenerator {
|
||||||
}
|
}
|
||||||
crate::parser::Literal::Int(i) => {
|
crate::parser::Literal::Int(i) => {
|
||||||
self.emit_byte(0x11); // PUSH_INT
|
self.emit_byte(0x11); // PUSH_INT
|
||||||
self.emit_u64(*i);
|
self.emit_u64(*i as u64);
|
||||||
}
|
}
|
||||||
crate::parser::Literal::Float(f) => {
|
crate::parser::Literal::Float(f) => {
|
||||||
self.emit_byte(0x12); // PUSH_FLOAT
|
self.emit_byte(0x12); // PUSH_FLOAT
|
||||||
|
|
@ -198,6 +199,9 @@ impl BytecodeGenerator {
|
||||||
self.emit_byte(0x10); // PUSH_BOOL
|
self.emit_byte(0x10); // PUSH_BOOL
|
||||||
self.emit_byte(1); // true
|
self.emit_byte(1); // true
|
||||||
}
|
}
|
||||||
|
Expression::Raw(_) => {
|
||||||
|
self.emit_byte(0x00);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
@ -251,11 +255,15 @@ mod tests {
|
||||||
id: "TEST".to_string(),
|
id: "TEST".to_string(),
|
||||||
level: ClauseLevel::Eternal,
|
level: ClauseLevel::Eternal,
|
||||||
title: "Test".to_string(),
|
title: "Test".to_string(),
|
||||||
|
name: None,
|
||||||
|
version: None,
|
||||||
|
description: None,
|
||||||
parameters: vec![],
|
parameters: vec![],
|
||||||
predicates: vec![],
|
predicates: vec![],
|
||||||
obligations: vec![],
|
obligations: vec![],
|
||||||
depends_on: vec![],
|
depends_on: vec![],
|
||||||
}],
|
}],
|
||||||
|
tests: vec![],
|
||||||
};
|
};
|
||||||
|
|
||||||
let bytecode = generator.generate_program(&program).expect("Bytecode generation failed");
|
let bytecode = generator.generate_program(&program).expect("Bytecode generation failed");
|
||||||
|
|
|
||||||
|
|
@ -1,169 +1,52 @@
|
||||||
|
//! CNNL抽象语法树(AST)定义
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
/// CNNL抽象语法树根节点
|
/// 程序(顶层节点)
|
||||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||||
pub struct Program {
|
pub struct Program {
|
||||||
pub clauses: Vec<Clause>,
|
pub clauses: Vec<Clause>,
|
||||||
|
/// 测试块(test "..." { assert ... })
|
||||||
|
#[serde(default)]
|
||||||
|
pub tests: Vec<TestBlock>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 宪法条款
|
/// 宪法条款
|
||||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||||
pub struct Clause {
|
pub struct Clause {
|
||||||
|
/// 条款标识符(全大写)
|
||||||
pub id: String,
|
pub id: String,
|
||||||
|
/// 条款层级
|
||||||
pub level: ClauseLevel,
|
pub level: ClauseLevel,
|
||||||
|
/// 条款标题
|
||||||
pub title: String,
|
pub title: String,
|
||||||
|
/// 条款名称(name: 字段,可选)
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub name: Option<String>,
|
||||||
|
/// 版本号(version: 字段,可选)
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub version: Option<String>,
|
||||||
|
/// 描述(description: 字段,可选)
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub description: Option<String>,
|
||||||
|
/// 依赖的其他条款
|
||||||
pub depends_on: Vec<String>,
|
pub depends_on: Vec<String>,
|
||||||
|
/// 参数列表
|
||||||
pub parameters: Vec<Parameter>,
|
pub parameters: Vec<Parameter>,
|
||||||
|
/// 谓词列表
|
||||||
pub predicates: Vec<Predicate>,
|
pub predicates: Vec<Predicate>,
|
||||||
|
/// 义务列表
|
||||||
pub obligations: Vec<Obligation>,
|
pub obligations: Vec<Obligation>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 条款层级
|
/// 条款层级
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
|
|
||||||
pub enum ClauseLevel {
|
|
||||||
Eternal, // 永恒条款
|
|
||||||
Strategic, // 战略条款
|
|
||||||
Tactical, // 战术条款
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 宪法参数
|
|
||||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
|
||||||
pub struct Parameter {
|
|
||||||
pub name: String,
|
|
||||||
pub ty: Type,
|
|
||||||
pub value: Literal,
|
|
||||||
pub description: Option<String>,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 宪法谓词
|
|
||||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
|
||||||
pub struct Predicate {
|
|
||||||
pub name: String,
|
|
||||||
pub params: Vec<(String, Type)>,
|
|
||||||
pub return_type: Type,
|
|
||||||
pub body: Expression,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 宪法义务
|
|
||||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
|
||||||
pub struct Obligation {
|
|
||||||
pub name: String,
|
|
||||||
pub frequency: ObligationFrequency,
|
|
||||||
pub enforcer: String,
|
|
||||||
pub penalty: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 义务频率
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
|
|
||||||
pub enum ObligationFrequency {
|
|
||||||
Continuous, // 持续
|
|
||||||
Periodic, // 周期性
|
|
||||||
OnDemand, // 按需
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 类型
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
pub enum Type {
|
pub enum ClauseLevel {
|
||||||
Bool,
|
/// 永恒条款(不可修改)
|
||||||
U64,
|
Eternal,
|
||||||
U32,
|
/// 战略条款(需要超级多数修改)
|
||||||
F64,
|
Strategic,
|
||||||
String,
|
/// 战术条款(普通多数修改)
|
||||||
}
|
Tactical,
|
||||||
|
|
||||||
/// 字面量
|
|
||||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
|
||||||
pub enum Literal {
|
|
||||||
Bool(bool),
|
|
||||||
Int(u64),
|
|
||||||
Float(f64),
|
|
||||||
String(String),
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 表达式
|
|
||||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
|
||||||
pub enum Expression {
|
|
||||||
// 字面量
|
|
||||||
Literal(Literal),
|
|
||||||
|
|
||||||
// 变量引用
|
|
||||||
Variable(String),
|
|
||||||
|
|
||||||
// 二元运算
|
|
||||||
Binary {
|
|
||||||
op: BinaryOp,
|
|
||||||
left: Box<Expression>,
|
|
||||||
right: Box<Expression>,
|
|
||||||
},
|
|
||||||
|
|
||||||
// 一元运算
|
|
||||||
Unary {
|
|
||||||
op: UnaryOp,
|
|
||||||
operand: Box<Expression>,
|
|
||||||
},
|
|
||||||
|
|
||||||
// 函数调用
|
|
||||||
Call {
|
|
||||||
name: String,
|
|
||||||
args: Vec<Expression>,
|
|
||||||
},
|
|
||||||
|
|
||||||
// if表达式
|
|
||||||
If {
|
|
||||||
condition: Box<Expression>,
|
|
||||||
then_branch: Box<Expression>,
|
|
||||||
else_branch: Option<Box<Expression>>,
|
|
||||||
},
|
|
||||||
|
|
||||||
// 代码块
|
|
||||||
Block(Vec<Statement>),
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 二元运算符
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
|
|
||||||
pub enum BinaryOp {
|
|
||||||
// 算术运算
|
|
||||||
Add,
|
|
||||||
Sub,
|
|
||||||
Mul,
|
|
||||||
Div,
|
|
||||||
Mod,
|
|
||||||
|
|
||||||
// 比较运算
|
|
||||||
Eq,
|
|
||||||
Ne,
|
|
||||||
Lt,
|
|
||||||
Le,
|
|
||||||
Gt,
|
|
||||||
Ge,
|
|
||||||
|
|
||||||
// 逻辑运算
|
|
||||||
And,
|
|
||||||
Or,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 一元运算符
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
|
|
||||||
pub enum UnaryOp {
|
|
||||||
Not,
|
|
||||||
Neg,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 语句
|
|
||||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
|
||||||
pub enum Statement {
|
|
||||||
// 表达式语句
|
|
||||||
Expression(Expression),
|
|
||||||
|
|
||||||
// 返回语句
|
|
||||||
Return(Expression),
|
|
||||||
|
|
||||||
// 变量声明
|
|
||||||
Let {
|
|
||||||
name: String,
|
|
||||||
ty: Option<Type>,
|
|
||||||
value: Expression,
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::fmt::Display for ClauseLevel {
|
impl std::fmt::Display for ClauseLevel {
|
||||||
|
|
@ -176,18 +59,167 @@ impl std::fmt::Display for ClauseLevel {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 参数
|
||||||
|
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||||
|
pub struct Parameter {
|
||||||
|
pub name: String,
|
||||||
|
/// 参数类型
|
||||||
|
pub ty: Type,
|
||||||
|
pub value: Literal,
|
||||||
|
/// 参数描述(可选)
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub description: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 谓词
|
||||||
|
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||||
|
pub struct Predicate {
|
||||||
|
pub name: String,
|
||||||
|
pub params: Vec<(String, Type)>,
|
||||||
|
pub return_type: Type,
|
||||||
|
/// 函数体(AST 表达式)
|
||||||
|
pub body: Expression,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 义务
|
||||||
|
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||||
|
pub struct Obligation {
|
||||||
|
pub name: String,
|
||||||
|
pub frequency: ObligationFrequency,
|
||||||
|
pub enforcer: String,
|
||||||
|
pub penalty: String,
|
||||||
|
/// 义务描述(可选)
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub description: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 义务频率
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
|
pub enum ObligationFrequency {
|
||||||
|
Continuous, // 持续
|
||||||
|
Periodic, // 周期性
|
||||||
|
OnDemand, // 按需
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::fmt::Display for ObligationFrequency {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||||
|
match self {
|
||||||
|
ObligationFrequency::Continuous => write!(f, "continuous"),
|
||||||
|
ObligationFrequency::Periodic => write!(f, "periodic"),
|
||||||
|
ObligationFrequency::OnDemand => write!(f, "on_demand"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 类型
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
|
pub enum Type {
|
||||||
|
Bool,
|
||||||
|
U64,
|
||||||
|
U32,
|
||||||
|
U128,
|
||||||
|
F64,
|
||||||
|
String,
|
||||||
|
}
|
||||||
|
|
||||||
impl std::fmt::Display for Type {
|
impl std::fmt::Display for Type {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||||
match self {
|
match self {
|
||||||
Type::Bool => write!(f, "bool"),
|
Type::Bool => write!(f, "bool"),
|
||||||
Type::U64 => write!(f, "u64"),
|
Type::U64 => write!(f, "u64"),
|
||||||
Type::U32 => write!(f, "u32"),
|
Type::U32 => write!(f, "u32"),
|
||||||
|
Type::U128 => write!(f, "u128"),
|
||||||
Type::F64 => write!(f, "f64"),
|
Type::F64 => write!(f, "f64"),
|
||||||
Type::String => write!(f, "string"),
|
Type::String => write!(f, "string"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 字面量
|
||||||
|
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||||
|
pub enum Literal {
|
||||||
|
Bool(bool),
|
||||||
|
Int(i64),
|
||||||
|
Float(f64),
|
||||||
|
String(String),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::fmt::Display for Literal {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||||
|
match self {
|
||||||
|
Literal::Bool(b) => write!(f, "{}", b),
|
||||||
|
Literal::Int(n) => write!(f, "{}", n),
|
||||||
|
Literal::Float(n) => write!(f, "{}", n),
|
||||||
|
Literal::String(s) => write!(f, "\"{}\"", s),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 表达式
|
||||||
|
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||||
|
pub enum Expression {
|
||||||
|
// 字面量
|
||||||
|
Literal(Literal),
|
||||||
|
|
||||||
|
// 变量引用
|
||||||
|
Variable(String),
|
||||||
|
|
||||||
|
// 二元运算
|
||||||
|
Binary {
|
||||||
|
op: BinaryOp,
|
||||||
|
left: Box<Expression>,
|
||||||
|
right: Box<Expression>,
|
||||||
|
},
|
||||||
|
|
||||||
|
// 一元运算
|
||||||
|
Unary {
|
||||||
|
op: UnaryOp,
|
||||||
|
operand: Box<Expression>,
|
||||||
|
},
|
||||||
|
|
||||||
|
// 函数调用
|
||||||
|
Call {
|
||||||
|
name: String,
|
||||||
|
args: Vec<Expression>,
|
||||||
|
},
|
||||||
|
|
||||||
|
// if表达式
|
||||||
|
If {
|
||||||
|
condition: Box<Expression>,
|
||||||
|
then_branch: Box<Expression>,
|
||||||
|
else_branch: Option<Box<Expression>>,
|
||||||
|
},
|
||||||
|
|
||||||
|
// 代码块
|
||||||
|
Block(Vec<Statement>),
|
||||||
|
|
||||||
|
// 原始字符串(解析器无法完整解析时的降级表示)
|
||||||
|
Raw(String),
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 二元运算符
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
|
pub enum BinaryOp {
|
||||||
|
// 算术运算
|
||||||
|
Add,
|
||||||
|
Sub,
|
||||||
|
Mul,
|
||||||
|
Div,
|
||||||
|
Mod,
|
||||||
|
|
||||||
|
// 比较运算
|
||||||
|
Eq,
|
||||||
|
Ne,
|
||||||
|
Lt,
|
||||||
|
Le,
|
||||||
|
Gt,
|
||||||
|
Ge,
|
||||||
|
|
||||||
|
// 逻辑运算
|
||||||
|
And,
|
||||||
|
Or,
|
||||||
|
}
|
||||||
|
|
||||||
impl std::fmt::Display for BinaryOp {
|
impl std::fmt::Display for BinaryOp {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||||
match self {
|
match self {
|
||||||
|
|
@ -208,6 +240,13 @@ impl std::fmt::Display for BinaryOp {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 一元运算符
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
|
pub enum UnaryOp {
|
||||||
|
Not,
|
||||||
|
Neg,
|
||||||
|
}
|
||||||
|
|
||||||
impl std::fmt::Display for UnaryOp {
|
impl std::fmt::Display for UnaryOp {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||||
match self {
|
match self {
|
||||||
|
|
@ -217,6 +256,30 @@ impl std::fmt::Display for UnaryOp {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 语句
|
||||||
|
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||||
|
pub enum Statement {
|
||||||
|
// 表达式语句
|
||||||
|
Expression(Expression),
|
||||||
|
|
||||||
|
// 返回语句
|
||||||
|
Return(Expression),
|
||||||
|
|
||||||
|
// 变量声明
|
||||||
|
Let {
|
||||||
|
name: String,
|
||||||
|
ty: Option<Type>,
|
||||||
|
value: Expression,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 测试块
|
||||||
|
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||||
|
pub struct TestBlock {
|
||||||
|
pub description: String,
|
||||||
|
pub assertions: Vec<String>,
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
@ -227,6 +290,9 @@ mod tests {
|
||||||
id: "XTZH_GOLD_COVERAGE".to_string(),
|
id: "XTZH_GOLD_COVERAGE".to_string(),
|
||||||
level: ClauseLevel::Eternal,
|
level: ClauseLevel::Eternal,
|
||||||
title: "黄金储备覆盖率底线".to_string(),
|
title: "黄金储备覆盖率底线".to_string(),
|
||||||
|
name: None,
|
||||||
|
version: None,
|
||||||
|
description: None,
|
||||||
depends_on: vec![],
|
depends_on: vec![],
|
||||||
parameters: vec![Parameter {
|
parameters: vec![Parameter {
|
||||||
name: "XTZH_GOLD_COVERAGE_MIN".to_string(),
|
name: "XTZH_GOLD_COVERAGE_MIN".to_string(),
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,4 @@
|
||||||
pub mod ast;
|
pub mod ast;
|
||||||
|
|
||||||
pub use ast::*;
|
pub use ast::*;
|
||||||
use crate::lexer::{Lexer, Token};
|
use crate::lexer::{Lexer, Token};
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
@ -9,13 +8,10 @@ use thiserror::Error;
|
||||||
pub enum ParserError {
|
pub enum ParserError {
|
||||||
#[error("Unexpected token: expected {expected}, found {found}")]
|
#[error("Unexpected token: expected {expected}, found {found}")]
|
||||||
UnexpectedToken { expected: String, found: String },
|
UnexpectedToken { expected: String, found: String },
|
||||||
|
|
||||||
#[error("Unexpected end of file")]
|
#[error("Unexpected end of file")]
|
||||||
UnexpectedEof,
|
UnexpectedEof,
|
||||||
|
|
||||||
#[error("Invalid syntax: {message}")]
|
#[error("Invalid syntax: {message}")]
|
||||||
InvalidSyntax { message: String },
|
InvalidSyntax { message: String },
|
||||||
|
|
||||||
#[error("Lexer error: {0}")]
|
#[error("Lexer error: {0}")]
|
||||||
LexerError(#[from] crate::lexer::LexerError),
|
LexerError(#[from] crate::lexer::LexerError),
|
||||||
}
|
}
|
||||||
|
|
@ -33,7 +29,6 @@ impl<'source> Parser<'source> {
|
||||||
let mut lexer = Lexer::new(source);
|
let mut lexer = Lexer::new(source);
|
||||||
let current = lexer.next().transpose()?;
|
let current = lexer.next().transpose()?;
|
||||||
let peek = lexer.next().transpose()?;
|
let peek = lexer.next().transpose()?;
|
||||||
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
lexer,
|
lexer,
|
||||||
current,
|
current,
|
||||||
|
|
@ -44,12 +39,23 @@ impl<'source> Parser<'source> {
|
||||||
/// 解析整个程序
|
/// 解析整个程序
|
||||||
pub fn parse(&mut self) -> Result<Program, ParserError> {
|
pub fn parse(&mut self) -> Result<Program, ParserError> {
|
||||||
let mut clauses = Vec::new();
|
let mut clauses = Vec::new();
|
||||||
|
let mut tests = Vec::new();
|
||||||
while self.current.is_some() {
|
while self.current.is_some() {
|
||||||
clauses.push(self.parse_clause()?);
|
match &self.current {
|
||||||
|
Some(Token::Clause) => {
|
||||||
|
clauses.push(self.parse_clause()?);
|
||||||
|
}
|
||||||
|
Some(Token::Test) => {
|
||||||
|
tests.push(self.parse_test_block()?);
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
return Err(ParserError::InvalidSyntax {
|
||||||
|
message: format!("Expected clause or test, found {:?}", self.current),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Ok(Program { clauses, tests })
|
||||||
Ok(Program { clauses })
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 解析条款
|
/// 解析条款
|
||||||
|
|
@ -61,6 +67,9 @@ impl<'source> Parser<'source> {
|
||||||
|
|
||||||
let mut level = ClauseLevel::Tactical;
|
let mut level = ClauseLevel::Tactical;
|
||||||
let mut title = String::new();
|
let mut title = String::new();
|
||||||
|
let mut name: Option<String> = None;
|
||||||
|
let mut version: Option<String> = None;
|
||||||
|
let mut description: Option<String> = None;
|
||||||
let mut depends_on = Vec::new();
|
let mut depends_on = Vec::new();
|
||||||
let mut parameters = Vec::new();
|
let mut parameters = Vec::new();
|
||||||
let mut predicates = Vec::new();
|
let mut predicates = Vec::new();
|
||||||
|
|
@ -79,6 +88,25 @@ impl<'source> Parser<'source> {
|
||||||
self.expect(Token::Colon)?;
|
self.expect(Token::Colon)?;
|
||||||
title = self.expect_string_literal()?;
|
title = self.expect_string_literal()?;
|
||||||
}
|
}
|
||||||
|
Some(Token::Name) => {
|
||||||
|
self.advance();
|
||||||
|
self.expect(Token::Colon)?;
|
||||||
|
let n = self.expect_string_literal()?;
|
||||||
|
if title.is_empty() {
|
||||||
|
title = n.clone();
|
||||||
|
}
|
||||||
|
name = Some(n);
|
||||||
|
}
|
||||||
|
Some(Token::Version) => {
|
||||||
|
self.advance();
|
||||||
|
self.expect(Token::Colon)?;
|
||||||
|
version = Some(self.expect_string_literal()?);
|
||||||
|
}
|
||||||
|
Some(Token::Description) => {
|
||||||
|
self.advance();
|
||||||
|
self.expect(Token::Colon)?;
|
||||||
|
description = Some(self.expect_string_literal()?);
|
||||||
|
}
|
||||||
Some(Token::DependsOn) => {
|
Some(Token::DependsOn) => {
|
||||||
self.advance();
|
self.advance();
|
||||||
self.expect(Token::Colon)?;
|
self.expect(Token::Colon)?;
|
||||||
|
|
@ -100,13 +128,15 @@ impl<'source> Parser<'source> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
self.expect(Token::RBrace)?;
|
self.expect(Token::RBrace)?;
|
||||||
|
|
||||||
Ok(Clause {
|
Ok(Clause {
|
||||||
id,
|
id,
|
||||||
level,
|
level,
|
||||||
title,
|
title,
|
||||||
|
name,
|
||||||
|
version,
|
||||||
|
description,
|
||||||
depends_on,
|
depends_on,
|
||||||
parameters,
|
parameters,
|
||||||
predicates,
|
predicates,
|
||||||
|
|
@ -145,7 +175,10 @@ impl<'source> Parser<'source> {
|
||||||
let ty = self.parse_type()?;
|
let ty = self.parse_type()?;
|
||||||
self.expect(Token::Assign)?;
|
self.expect(Token::Assign)?;
|
||||||
let value = self.parse_literal()?;
|
let value = self.parse_literal()?;
|
||||||
|
// 可选分号
|
||||||
|
if self.check(&Token::Semicolon) {
|
||||||
|
self.advance();
|
||||||
|
}
|
||||||
Ok(Parameter {
|
Ok(Parameter {
|
||||||
name,
|
name,
|
||||||
ty,
|
ty,
|
||||||
|
|
@ -160,29 +193,23 @@ impl<'source> Parser<'source> {
|
||||||
self.expect(Token::Predicate)?;
|
self.expect(Token::Predicate)?;
|
||||||
let name = self.expect_ident()?;
|
let name = self.expect_ident()?;
|
||||||
self.expect(Token::LParen)?;
|
self.expect(Token::LParen)?;
|
||||||
|
|
||||||
let mut params = Vec::new();
|
let mut params = Vec::new();
|
||||||
while !self.check(&Token::RParen) {
|
while !self.check(&Token::RParen) {
|
||||||
let param_name = self.expect_ident()?;
|
let param_name = self.expect_ident()?;
|
||||||
self.expect(Token::Colon)?;
|
self.expect(Token::Colon)?;
|
||||||
let param_type = self.parse_type()?;
|
let param_type = self.parse_type()?;
|
||||||
params.push((param_name, param_type));
|
params.push((param_name, param_type));
|
||||||
|
|
||||||
if self.check(&Token::Comma) {
|
if self.check(&Token::Comma) {
|
||||||
self.advance();
|
self.advance();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
self.expect(Token::RParen)?;
|
self.expect(Token::RParen)?;
|
||||||
self.expect(Token::Arrow)?;
|
self.expect(Token::Arrow)?;
|
||||||
let return_type = self.parse_type()?;
|
let return_type = self.parse_type()?;
|
||||||
self.expect(Token::LBrace)?;
|
self.expect(Token::LBrace)?;
|
||||||
|
|
||||||
// 简化:只解析单个返回表达式
|
// 简化:只解析单个返回表达式
|
||||||
let body = self.parse_expression()?;
|
let body = self.parse_expression()?;
|
||||||
|
|
||||||
self.expect(Token::RBrace)?;
|
self.expect(Token::RBrace)?;
|
||||||
|
|
||||||
Ok(Predicate {
|
Ok(Predicate {
|
||||||
name,
|
name,
|
||||||
params,
|
params,
|
||||||
|
|
@ -201,6 +228,7 @@ impl<'source> Parser<'source> {
|
||||||
let mut frequency = ObligationFrequency::OnDemand;
|
let mut frequency = ObligationFrequency::OnDemand;
|
||||||
let mut enforcer = String::new();
|
let mut enforcer = String::new();
|
||||||
let mut penalty = String::new();
|
let mut penalty = String::new();
|
||||||
|
let mut description: Option<String> = None;
|
||||||
|
|
||||||
while !self.check(&Token::RBrace) {
|
while !self.check(&Token::RBrace) {
|
||||||
match &self.current {
|
match &self.current {
|
||||||
|
|
@ -219,18 +247,21 @@ impl<'source> Parser<'source> {
|
||||||
self.expect(Token::Colon)?;
|
self.expect(Token::Colon)?;
|
||||||
penalty = self.expect_ident()?;
|
penalty = self.expect_ident()?;
|
||||||
}
|
}
|
||||||
|
Some(Token::Description) => {
|
||||||
|
self.advance();
|
||||||
|
self.expect(Token::Colon)?;
|
||||||
|
description = Some(self.expect_string_literal()?);
|
||||||
|
}
|
||||||
_ => {
|
_ => {
|
||||||
return Err(ParserError::InvalidSyntax {
|
return Err(ParserError::InvalidSyntax {
|
||||||
message: format!("Unexpected token in obligation: {:?}", self.current),
|
message: format!("Unexpected token in obligation: {:?}", self.current),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.check(&Token::Comma) {
|
if self.check(&Token::Comma) {
|
||||||
self.advance();
|
self.advance();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
self.expect(Token::RBrace)?;
|
self.expect(Token::RBrace)?;
|
||||||
|
|
||||||
Ok(Obligation {
|
Ok(Obligation {
|
||||||
|
|
@ -238,6 +269,7 @@ impl<'source> Parser<'source> {
|
||||||
frequency,
|
frequency,
|
||||||
enforcer,
|
enforcer,
|
||||||
penalty,
|
penalty,
|
||||||
|
description,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -263,6 +295,50 @@ impl<'source> Parser<'source> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 解析测试块
|
||||||
|
fn parse_test_block(&mut self) -> Result<TestBlock, ParserError> {
|
||||||
|
// test "DESCRIPTION" { assert EXPR ; ... }
|
||||||
|
self.expect(Token::Test)?;
|
||||||
|
let description = self.expect_string_literal()?;
|
||||||
|
self.expect(Token::LBrace)?;
|
||||||
|
let mut assertions = Vec::new();
|
||||||
|
while !self.check(&Token::RBrace) {
|
||||||
|
if self.check(&Token::Assert) {
|
||||||
|
self.advance();
|
||||||
|
let expr_str = self.collect_until_semicolon()?;
|
||||||
|
assertions.push(expr_str);
|
||||||
|
} else {
|
||||||
|
return Err(ParserError::InvalidSyntax {
|
||||||
|
message: format!("Expected assert in test block, found {:?}", self.current),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.expect(Token::RBrace)?;
|
||||||
|
Ok(TestBlock { description, assertions })
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 收集 token 直到分号
|
||||||
|
fn collect_until_semicolon(&mut self) -> Result<String, ParserError> {
|
||||||
|
let mut tokens = Vec::new();
|
||||||
|
loop {
|
||||||
|
match &self.current {
|
||||||
|
Some(Token::Semicolon) => {
|
||||||
|
self.advance();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
Some(Token::RBrace) => {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
Some(t) => {
|
||||||
|
tokens.push(format!("{}", t));
|
||||||
|
self.advance();
|
||||||
|
}
|
||||||
|
None => break,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(tokens.join(" "))
|
||||||
|
}
|
||||||
|
|
||||||
/// 解析表达式(简化版)
|
/// 解析表达式(简化版)
|
||||||
fn parse_expression(&mut self) -> Result<Expression, ParserError> {
|
fn parse_expression(&mut self) -> Result<Expression, ParserError> {
|
||||||
self.parse_comparison()
|
self.parse_comparison()
|
||||||
|
|
@ -271,7 +347,6 @@ impl<'source> Parser<'source> {
|
||||||
/// 解析比较表达式
|
/// 解析比较表达式
|
||||||
fn parse_comparison(&mut self) -> Result<Expression, ParserError> {
|
fn parse_comparison(&mut self) -> Result<Expression, ParserError> {
|
||||||
let mut left = self.parse_term()?;
|
let mut left = self.parse_term()?;
|
||||||
|
|
||||||
while let Some(token) = &self.current {
|
while let Some(token) = &self.current {
|
||||||
let op = match token {
|
let op = match token {
|
||||||
Token::Eq => BinaryOp::Eq,
|
Token::Eq => BinaryOp::Eq,
|
||||||
|
|
@ -282,7 +357,6 @@ impl<'source> Parser<'source> {
|
||||||
Token::Ge => BinaryOp::Ge,
|
Token::Ge => BinaryOp::Ge,
|
||||||
_ => break,
|
_ => break,
|
||||||
};
|
};
|
||||||
|
|
||||||
self.advance();
|
self.advance();
|
||||||
let right = self.parse_term()?;
|
let right = self.parse_term()?;
|
||||||
left = Expression::Binary {
|
left = Expression::Binary {
|
||||||
|
|
@ -291,21 +365,18 @@ impl<'source> Parser<'source> {
|
||||||
right: Box::new(right),
|
right: Box::new(right),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(left)
|
Ok(left)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 解析项
|
/// 解析项
|
||||||
fn parse_term(&mut self) -> Result<Expression, ParserError> {
|
fn parse_term(&mut self) -> Result<Expression, ParserError> {
|
||||||
let mut left = self.parse_factor()?;
|
let mut left = self.parse_factor()?;
|
||||||
|
|
||||||
while let Some(token) = &self.current {
|
while let Some(token) = &self.current {
|
||||||
let op = match token {
|
let op = match token {
|
||||||
Token::Plus => BinaryOp::Add,
|
Token::Plus => BinaryOp::Add,
|
||||||
Token::Minus => BinaryOp::Sub,
|
Token::Minus => BinaryOp::Sub,
|
||||||
_ => break,
|
_ => break,
|
||||||
};
|
};
|
||||||
|
|
||||||
self.advance();
|
self.advance();
|
||||||
let right = self.parse_factor()?;
|
let right = self.parse_factor()?;
|
||||||
left = Expression::Binary {
|
left = Expression::Binary {
|
||||||
|
|
@ -314,60 +385,80 @@ impl<'source> Parser<'source> {
|
||||||
right: Box::new(right),
|
right: Box::new(right),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(left)
|
Ok(left)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 解析因子
|
/// 解析因子
|
||||||
fn parse_factor(&mut self) -> Result<Expression, ParserError> {
|
fn parse_factor(&mut self) -> Result<Expression, ParserError> {
|
||||||
let mut left = self.parse_primary()?;
|
let mut left = self.parse_unary()?;
|
||||||
|
|
||||||
while let Some(token) = &self.current {
|
while let Some(token) = &self.current {
|
||||||
let op = match token {
|
let op = match token {
|
||||||
Token::Star => BinaryOp::Mul,
|
Token::Star => BinaryOp::Mul,
|
||||||
Token::Slash => BinaryOp::Div,
|
Token::Slash => BinaryOp::Div,
|
||||||
|
Token::Percent => BinaryOp::Mod,
|
||||||
_ => break,
|
_ => break,
|
||||||
};
|
};
|
||||||
|
|
||||||
self.advance();
|
self.advance();
|
||||||
let right = self.parse_primary()?;
|
let right = self.parse_unary()?;
|
||||||
left = Expression::Binary {
|
left = Expression::Binary {
|
||||||
op,
|
op,
|
||||||
left: Box::new(left),
|
left: Box::new(left),
|
||||||
right: Box::new(right),
|
right: Box::new(right),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(left)
|
Ok(left)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 解析一元表达式
|
||||||
|
fn parse_unary(&mut self) -> Result<Expression, ParserError> {
|
||||||
|
match &self.current {
|
||||||
|
Some(Token::Not) => {
|
||||||
|
self.advance();
|
||||||
|
let operand = self.parse_primary()?;
|
||||||
|
Ok(Expression::Unary {
|
||||||
|
op: UnaryOp::Not,
|
||||||
|
operand: Box::new(operand),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
Some(Token::Minus) => {
|
||||||
|
self.advance();
|
||||||
|
let operand = self.parse_primary()?;
|
||||||
|
Ok(Expression::Unary {
|
||||||
|
op: UnaryOp::Neg,
|
||||||
|
operand: Box::new(operand),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
_ => self.parse_primary(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// 解析基本表达式
|
/// 解析基本表达式
|
||||||
fn parse_primary(&mut self) -> Result<Expression, ParserError> {
|
fn parse_primary(&mut self) -> Result<Expression, ParserError> {
|
||||||
match &self.current {
|
match &self.current {
|
||||||
Some(Token::IntLiteral(n)) => {
|
Some(Token::IntLiteral(_))
|
||||||
let n = *n;
|
| Some(Token::FloatLiteral(_))
|
||||||
self.advance();
|
| Some(Token::BoolLiteral(_))
|
||||||
Ok(Expression::Literal(Literal::Int(n)))
|
| Some(Token::StringLiteral(_)) => {
|
||||||
|
let lit = self.parse_literal()?;
|
||||||
|
Ok(Expression::Literal(lit))
|
||||||
}
|
}
|
||||||
Some(Token::FloatLiteral(f)) => {
|
Some(Token::Ident(_)) | Some(Token::ConstantIdent(_)) => {
|
||||||
let f = *f;
|
let name = self.expect_ident()?;
|
||||||
self.advance();
|
// 检查是否是函数调用
|
||||||
Ok(Expression::Literal(Literal::Float(f)))
|
if self.check(&Token::LParen) {
|
||||||
}
|
self.advance();
|
||||||
Some(Token::BoolLiteral(b)) => {
|
let mut args = Vec::new();
|
||||||
let b = *b;
|
while !self.check(&Token::RParen) {
|
||||||
self.advance();
|
args.push(self.parse_expression()?);
|
||||||
Ok(Expression::Literal(Literal::Bool(b)))
|
if self.check(&Token::Comma) {
|
||||||
}
|
self.advance();
|
||||||
Some(Token::StringLiteral(s)) => {
|
}
|
||||||
let s = s.clone();
|
}
|
||||||
self.advance();
|
self.expect(Token::RParen)?;
|
||||||
Ok(Expression::Literal(Literal::String(s)))
|
Ok(Expression::Call { name, args })
|
||||||
}
|
} else {
|
||||||
Some(Token::Ident(name)) | Some(Token::ConstantIdent(name)) => {
|
Ok(Expression::Variable(name))
|
||||||
let name = name.clone();
|
}
|
||||||
self.advance();
|
|
||||||
Ok(Expression::Variable(name))
|
|
||||||
}
|
}
|
||||||
Some(Token::LParen) => {
|
Some(Token::LParen) => {
|
||||||
self.advance();
|
self.advance();
|
||||||
|
|
@ -397,6 +488,10 @@ impl<'source> Parser<'source> {
|
||||||
self.advance();
|
self.advance();
|
||||||
Ok(Type::U32)
|
Ok(Type::U32)
|
||||||
}
|
}
|
||||||
|
Some(Token::U128) => {
|
||||||
|
self.advance();
|
||||||
|
Ok(Type::U128)
|
||||||
|
}
|
||||||
Some(Token::F64) => {
|
Some(Token::F64) => {
|
||||||
self.advance();
|
self.advance();
|
||||||
Ok(Type::F64)
|
Ok(Type::F64)
|
||||||
|
|
@ -446,15 +541,12 @@ impl<'source> Parser<'source> {
|
||||||
fn parse_string_array(&mut self) -> Result<Vec<String>, ParserError> {
|
fn parse_string_array(&mut self) -> Result<Vec<String>, ParserError> {
|
||||||
self.expect(Token::LBracket)?;
|
self.expect(Token::LBracket)?;
|
||||||
let mut strings = Vec::new();
|
let mut strings = Vec::new();
|
||||||
|
|
||||||
while !self.check(&Token::RBracket) {
|
while !self.check(&Token::RBracket) {
|
||||||
strings.push(self.expect_string_literal()?);
|
strings.push(self.expect_string_literal()?);
|
||||||
|
|
||||||
if self.check(&Token::Comma) {
|
if self.check(&Token::Comma) {
|
||||||
self.advance();
|
self.advance();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
self.expect(Token::RBracket)?;
|
self.expect(Token::RBracket)?;
|
||||||
Ok(strings)
|
Ok(strings)
|
||||||
}
|
}
|
||||||
|
|
@ -546,16 +638,41 @@ mod tests {
|
||||||
parameter XTZH_GOLD_COVERAGE_MIN: f64 = 1.25
|
parameter XTZH_GOLD_COVERAGE_MIN: f64 = 1.25
|
||||||
}
|
}
|
||||||
"#;
|
"#;
|
||||||
|
|
||||||
let mut parser = Parser::new(source).expect("Parser creation failed");
|
let mut parser = Parser::new(source).expect("Parser creation failed");
|
||||||
let program = parser.parse().expect("Parse failed");
|
let program = parser.parse().expect("Parse failed");
|
||||||
|
|
||||||
assert_eq!(program.clauses.len(), 1);
|
assert_eq!(program.clauses.len(), 1);
|
||||||
assert_eq!(program.clauses[0].id, "XTZH_GOLD_COVERAGE");
|
assert_eq!(program.clauses[0].id, "XTZH_GOLD_COVERAGE");
|
||||||
assert_eq!(program.clauses[0].level, ClauseLevel::Eternal);
|
assert_eq!(program.clauses[0].level, ClauseLevel::Eternal);
|
||||||
assert_eq!(program.clauses[0].parameters.len(), 1);
|
assert_eq!(program.clauses[0].parameters.len(), 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_parse_name_version() {
|
||||||
|
let source = r#"
|
||||||
|
clause CORE_GOVERNANCE {
|
||||||
|
name: "核心治理条款"
|
||||||
|
version: "1.0.0"
|
||||||
|
level: eternal
|
||||||
|
title: "核心治理"
|
||||||
|
parameter MAX_VALIDATORS: u32 = 100
|
||||||
|
parameter MIN_STAKE: u128 = 1000000000000000000
|
||||||
|
obligation VALIDATOR_REGISTRATION {
|
||||||
|
frequency: continuous
|
||||||
|
enforcer: ai
|
||||||
|
penalty: suspension
|
||||||
|
description: "验证者必须质押最小金额"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"#;
|
||||||
|
let mut parser = Parser::new(source).expect("Parser creation failed");
|
||||||
|
let program = parser.parse().expect("Parse failed");
|
||||||
|
assert_eq!(program.clauses.len(), 1);
|
||||||
|
assert_eq!(program.clauses[0].id, "CORE_GOVERNANCE");
|
||||||
|
assert_eq!(program.clauses[0].name, Some("核心治理条款".to_string()));
|
||||||
|
assert_eq!(program.clauses[0].version, Some("1.0.0".to_string()));
|
||||||
|
assert_eq!(program.clauses[0].parameters.len(), 2);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_parse_predicate() {
|
fn test_parse_predicate() {
|
||||||
let source = r#"
|
let source = r#"
|
||||||
|
|
@ -567,11 +684,24 @@ mod tests {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
"#;
|
"#;
|
||||||
|
|
||||||
let mut parser = Parser::new(source).expect("Parser creation failed");
|
let mut parser = Parser::new(source).expect("Parser creation failed");
|
||||||
let program = parser.parse().expect("Parse failed");
|
let program = parser.parse().expect("Parse failed");
|
||||||
|
|
||||||
assert_eq!(program.clauses[0].predicates.len(), 1);
|
assert_eq!(program.clauses[0].predicates.len(), 1);
|
||||||
assert_eq!(program.clauses[0].predicates[0].name, "check_value");
|
assert_eq!(program.clauses[0].predicates[0].name, "check_value");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_parse_test_block() {
|
||||||
|
let source = r#"
|
||||||
|
test "核心治理参数一致性" {
|
||||||
|
assert MAX_VALIDATORS > 0 ;
|
||||||
|
assert MIN_STAKE >= 1000000000000000000 ;
|
||||||
|
}
|
||||||
|
"#;
|
||||||
|
let mut parser = Parser::new(source).expect("Parser creation failed");
|
||||||
|
let program = parser.parse().expect("Parse failed");
|
||||||
|
assert_eq!(program.tests.len(), 1);
|
||||||
|
assert_eq!(program.tests[0].description, "核心治理参数一致性");
|
||||||
|
assert_eq!(program.tests[0].assertions.len(), 2);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -124,6 +124,9 @@ mod tests {
|
||||||
id: "TEST".to_string(),
|
id: "TEST".to_string(),
|
||||||
level: ClauseLevel::Eternal,
|
level: ClauseLevel::Eternal,
|
||||||
title: "Test".to_string(),
|
title: "Test".to_string(),
|
||||||
|
name: None,
|
||||||
|
version: None,
|
||||||
|
description: None,
|
||||||
parameters: vec![],
|
parameters: vec![],
|
||||||
predicates: vec![],
|
predicates: vec![],
|
||||||
obligations: vec![Obligation {
|
obligations: vec![Obligation {
|
||||||
|
|
@ -131,6 +134,7 @@ mod tests {
|
||||||
frequency: crate::parser::ObligationFrequency::Continuous,
|
frequency: crate::parser::ObligationFrequency::Continuous,
|
||||||
enforcer: "ai_system".to_string(),
|
enforcer: "ai_system".to_string(),
|
||||||
penalty: "suspension".to_string(),
|
penalty: "suspension".to_string(),
|
||||||
|
description: None,
|
||||||
}],
|
}],
|
||||||
depends_on: vec![],
|
depends_on: vec![],
|
||||||
};
|
};
|
||||||
|
|
@ -146,6 +150,9 @@ mod tests {
|
||||||
id: "TEST".to_string(),
|
id: "TEST".to_string(),
|
||||||
level: ClauseLevel::Eternal,
|
level: ClauseLevel::Eternal,
|
||||||
title: "Test".to_string(),
|
title: "Test".to_string(),
|
||||||
|
name: None,
|
||||||
|
version: None,
|
||||||
|
description: None,
|
||||||
parameters: vec![],
|
parameters: vec![],
|
||||||
predicates: vec![],
|
predicates: vec![],
|
||||||
obligations: vec![Obligation {
|
obligations: vec![Obligation {
|
||||||
|
|
@ -153,6 +160,7 @@ mod tests {
|
||||||
frequency: crate::parser::ObligationFrequency::Continuous,
|
frequency: crate::parser::ObligationFrequency::Continuous,
|
||||||
enforcer: "".to_string(),
|
enforcer: "".to_string(),
|
||||||
penalty: "suspension".to_string(),
|
penalty: "suspension".to_string(),
|
||||||
|
description: None,
|
||||||
}],
|
}],
|
||||||
depends_on: vec![],
|
depends_on: vec![],
|
||||||
};
|
};
|
||||||
|
|
@ -168,6 +176,9 @@ mod tests {
|
||||||
id: "TEST".to_string(),
|
id: "TEST".to_string(),
|
||||||
level: ClauseLevel::Eternal,
|
level: ClauseLevel::Eternal,
|
||||||
title: "Test".to_string(),
|
title: "Test".to_string(),
|
||||||
|
name: None,
|
||||||
|
version: None,
|
||||||
|
description: None,
|
||||||
parameters: vec![],
|
parameters: vec![],
|
||||||
predicates: vec![],
|
predicates: vec![],
|
||||||
obligations: vec![Obligation {
|
obligations: vec![Obligation {
|
||||||
|
|
@ -175,6 +186,7 @@ mod tests {
|
||||||
frequency: crate::parser::ObligationFrequency::Continuous,
|
frequency: crate::parser::ObligationFrequency::Continuous,
|
||||||
enforcer: "manual".to_string(),
|
enforcer: "manual".to_string(),
|
||||||
penalty: "suspension".to_string(),
|
penalty: "suspension".to_string(),
|
||||||
|
description: None,
|
||||||
}],
|
}],
|
||||||
depends_on: vec![],
|
depends_on: vec![],
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -161,6 +161,7 @@ impl ScopeResolver {
|
||||||
Expression::Literal(_) => {
|
Expression::Literal(_) => {
|
||||||
// 字面量不需要解析
|
// 字面量不需要解析
|
||||||
}
|
}
|
||||||
|
crate::parser::Expression::Raw(_) => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -255,6 +256,9 @@ mod tests {
|
||||||
id: "TEST".to_string(),
|
id: "TEST".to_string(),
|
||||||
level: ClauseLevel::Eternal,
|
level: ClauseLevel::Eternal,
|
||||||
title: "Test".to_string(),
|
title: "Test".to_string(),
|
||||||
|
name: None,
|
||||||
|
version: None,
|
||||||
|
description: None,
|
||||||
parameters: vec![Parameter {
|
parameters: vec![Parameter {
|
||||||
name: "x".to_string(),
|
name: "x".to_string(),
|
||||||
ty: Type::U64,
|
ty: Type::U64,
|
||||||
|
|
|
||||||
|
|
@ -132,6 +132,7 @@ impl TypeChecker {
|
||||||
Expression::Call { name: _, args: _ } => Type::Bool,
|
Expression::Call { name: _, args: _ } => Type::Bool,
|
||||||
Expression::If { .. } => Type::Bool,
|
Expression::If { .. } => Type::Bool,
|
||||||
Expression::Block(_) => Type::Bool,
|
Expression::Block(_) => Type::Bool,
|
||||||
|
crate::parser::Expression::Raw(_) => Type::Bool,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -155,6 +156,9 @@ mod tests {
|
||||||
id: "TEST".to_string(),
|
id: "TEST".to_string(),
|
||||||
level: crate::parser::ClauseLevel::Eternal,
|
level: crate::parser::ClauseLevel::Eternal,
|
||||||
title: "Test".to_string(),
|
title: "Test".to_string(),
|
||||||
|
name: None,
|
||||||
|
version: None,
|
||||||
|
description: None,
|
||||||
parameters: vec![Parameter {
|
parameters: vec![Parameter {
|
||||||
name: "x".to_string(),
|
name: "x".to_string(),
|
||||||
ty: Type::U64,
|
ty: Type::U64,
|
||||||
|
|
@ -182,6 +186,9 @@ mod tests {
|
||||||
id: "TEST".to_string(),
|
id: "TEST".to_string(),
|
||||||
level: crate::parser::ClauseLevel::Eternal,
|
level: crate::parser::ClauseLevel::Eternal,
|
||||||
title: "Test".to_string(),
|
title: "Test".to_string(),
|
||||||
|
name: None,
|
||||||
|
version: None,
|
||||||
|
description: None,
|
||||||
parameters: vec![Parameter {
|
parameters: vec![Parameter {
|
||||||
name: "x".to_string(),
|
name: "x".to_string(),
|
||||||
ty: Type::U64,
|
ty: Type::U64,
|
||||||
|
|
|
||||||
|
|
@ -95,6 +95,7 @@ impl ConstraintGenerator {
|
||||||
Expression::Block(_) => {
|
Expression::Block(_) => {
|
||||||
"true".to_string() // 简化处理
|
"true".to_string() // 简化处理
|
||||||
}
|
}
|
||||||
|
Expression::Raw(s) => s.clone(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -137,6 +138,9 @@ mod tests {
|
||||||
id: "TEST".to_string(),
|
id: "TEST".to_string(),
|
||||||
level: ClauseLevel::Eternal,
|
level: ClauseLevel::Eternal,
|
||||||
title: "Test".to_string(),
|
title: "Test".to_string(),
|
||||||
|
name: None,
|
||||||
|
version: None,
|
||||||
|
description: None,
|
||||||
parameters: vec![],
|
parameters: vec![],
|
||||||
predicates: vec![Predicate {
|
predicates: vec![Predicate {
|
||||||
name: "test_pred".to_string(),
|
name: "test_pred".to_string(),
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,39 @@
|
||||||
|
[package]
|
||||||
|
name = "nac-cnnl-service"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
authors = ["NAC Core Team <dev@newassetchain.io>"]
|
||||||
|
description = "CNNL HTTP Service - RESTful API for CNNL Compiler"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
# HTTP 框架
|
||||||
|
actix-web = "4"
|
||||||
|
actix-rt = "2"
|
||||||
|
|
||||||
|
# 序列化
|
||||||
|
serde = { version = "1.0", features = ["derive"] }
|
||||||
|
serde_json = "1.0"
|
||||||
|
|
||||||
|
# 错误处理
|
||||||
|
thiserror = "1.0"
|
||||||
|
anyhow = "1.0"
|
||||||
|
|
||||||
|
# 日志
|
||||||
|
log = "0.4"
|
||||||
|
env_logger = "0.11"
|
||||||
|
|
||||||
|
# 时间
|
||||||
|
chrono = { version = "0.4", features = ["serde"] }
|
||||||
|
hex = "0.4"
|
||||||
|
|
||||||
|
# CNNL 编译器库(本地路径)
|
||||||
|
cnnl-compiler = { path = "../cnnl-compiler" }
|
||||||
|
|
||||||
|
[[bin]]
|
||||||
|
name = "nac-cnnl-service"
|
||||||
|
path = "src/main.rs"
|
||||||
|
|
||||||
|
[profile.release]
|
||||||
|
opt-level = 3
|
||||||
|
lto = true
|
||||||
|
codegen-units = 1
|
||||||
|
|
@ -0,0 +1,432 @@
|
||||||
|
//! NAC CNNL HTTP 服务
|
||||||
|
//!
|
||||||
|
//! 将 CNNL 编译器封装为 RESTful HTTP API,供立法 IDE、宪法沙箱等工具调用。
|
||||||
|
//!
|
||||||
|
//! # 接口列表
|
||||||
|
//! - POST /api/v1/compile - 编译 CNNL 源代码
|
||||||
|
//! - POST /api/v1/parse - 解析 CNNL 源代码(仅返回 AST)
|
||||||
|
//! - POST /api/v1/validate - 验证 CNNL 语法(不生成字节码)
|
||||||
|
//! - GET /api/v1/health - 健康检查
|
||||||
|
//! - GET /api/v1/version - 版本信息
|
||||||
|
|
||||||
|
use actix_web::{web, App, HttpServer, HttpResponse, Responder, middleware};
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use std::time::Instant;
|
||||||
|
use chrono::Utc;
|
||||||
|
use log::{info, warn, error};
|
||||||
|
|
||||||
|
// ============================================================
|
||||||
|
// 请求/响应数据结构
|
||||||
|
// ============================================================
|
||||||
|
|
||||||
|
/// 编译请求
|
||||||
|
#[derive(Debug, Deserialize)]
|
||||||
|
pub struct CompileRequest {
|
||||||
|
/// CNNL 源代码
|
||||||
|
pub source: String,
|
||||||
|
/// 是否启用形式化验证
|
||||||
|
#[serde(default)]
|
||||||
|
pub enable_verification: bool,
|
||||||
|
/// 是否生成调试信息
|
||||||
|
#[serde(default)]
|
||||||
|
pub debug_info: bool,
|
||||||
|
/// 是否生成宪法状态文件
|
||||||
|
#[serde(default = "default_true")]
|
||||||
|
pub generate_state: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn default_true() -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 解析请求
|
||||||
|
#[derive(Debug, Deserialize)]
|
||||||
|
pub struct ParseRequest {
|
||||||
|
/// CNNL 源代码
|
||||||
|
pub source: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 验证请求
|
||||||
|
#[derive(Debug, Deserialize)]
|
||||||
|
pub struct ValidateRequest {
|
||||||
|
/// CNNL 源代码
|
||||||
|
pub source: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 通用 API 响应
|
||||||
|
#[derive(Debug, Serialize)]
|
||||||
|
pub struct ApiResponse<T: Serialize> {
|
||||||
|
pub success: bool,
|
||||||
|
pub data: Option<T>,
|
||||||
|
pub error: Option<String>,
|
||||||
|
pub timestamp: String,
|
||||||
|
pub duration_ms: u64,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Serialize> ApiResponse<T> {
|
||||||
|
pub fn ok(data: T, duration_ms: u64) -> Self {
|
||||||
|
Self {
|
||||||
|
success: true,
|
||||||
|
data: Some(data),
|
||||||
|
error: None,
|
||||||
|
timestamp: Utc::now().to_rfc3339(),
|
||||||
|
duration_ms,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn err(msg: String, duration_ms: u64) -> ApiResponse<serde_json::Value> {
|
||||||
|
ApiResponse {
|
||||||
|
success: false,
|
||||||
|
data: None,
|
||||||
|
error: Some(msg),
|
||||||
|
timestamp: Utc::now().to_rfc3339(),
|
||||||
|
duration_ms,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 编译结果
|
||||||
|
#[derive(Debug, Serialize)]
|
||||||
|
pub struct CompileResult {
|
||||||
|
/// 字节码(hex 编码)
|
||||||
|
pub bytecode_hex: String,
|
||||||
|
/// 字节码长度(字节)
|
||||||
|
pub bytecode_size: usize,
|
||||||
|
/// 宪法状态 JSON(可选)
|
||||||
|
pub state_json: Option<serde_json::Value>,
|
||||||
|
/// 条款数量
|
||||||
|
pub clause_count: usize,
|
||||||
|
/// 条款摘要
|
||||||
|
pub clauses: Vec<ClauseSummary>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 解析结果
|
||||||
|
#[derive(Debug, Serialize)]
|
||||||
|
pub struct ParseResult {
|
||||||
|
/// 条款数量
|
||||||
|
pub clause_count: usize,
|
||||||
|
/// 测试块数量
|
||||||
|
pub test_count: usize,
|
||||||
|
/// 条款详情
|
||||||
|
pub clauses: Vec<ClauseDetail>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 验证结果
|
||||||
|
#[derive(Debug, Serialize)]
|
||||||
|
pub struct ValidateResult {
|
||||||
|
/// 是否有效
|
||||||
|
pub valid: bool,
|
||||||
|
/// 错误列表
|
||||||
|
pub errors: Vec<String>,
|
||||||
|
/// 警告列表
|
||||||
|
pub warnings: Vec<String>,
|
||||||
|
/// 条款数量
|
||||||
|
pub clause_count: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 条款摘要
|
||||||
|
#[derive(Debug, Serialize)]
|
||||||
|
pub struct ClauseSummary {
|
||||||
|
pub id: String,
|
||||||
|
pub level: String,
|
||||||
|
pub title: String,
|
||||||
|
pub parameter_count: usize,
|
||||||
|
pub predicate_count: usize,
|
||||||
|
pub obligation_count: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 条款详情
|
||||||
|
#[derive(Debug, Serialize)]
|
||||||
|
pub struct ClauseDetail {
|
||||||
|
pub id: String,
|
||||||
|
pub level: String,
|
||||||
|
pub title: String,
|
||||||
|
pub name: Option<String>,
|
||||||
|
pub version: Option<String>,
|
||||||
|
pub description: Option<String>,
|
||||||
|
pub depends_on: Vec<String>,
|
||||||
|
pub parameters: Vec<ParameterDetail>,
|
||||||
|
pub predicates: Vec<PredicateDetail>,
|
||||||
|
pub obligations: Vec<ObligationDetail>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 参数详情
|
||||||
|
#[derive(Debug, Serialize)]
|
||||||
|
pub struct ParameterDetail {
|
||||||
|
pub name: String,
|
||||||
|
pub type_name: String,
|
||||||
|
pub value: String,
|
||||||
|
pub description: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 谓词详情
|
||||||
|
#[derive(Debug, Serialize)]
|
||||||
|
pub struct PredicateDetail {
|
||||||
|
pub name: String,
|
||||||
|
pub params: Vec<(String, String)>,
|
||||||
|
pub return_type: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 义务详情
|
||||||
|
#[derive(Debug, Serialize)]
|
||||||
|
pub struct ObligationDetail {
|
||||||
|
pub name: String,
|
||||||
|
pub frequency: String,
|
||||||
|
pub enforcer: String,
|
||||||
|
pub penalty: String,
|
||||||
|
pub description: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 版本信息
|
||||||
|
#[derive(Debug, Serialize)]
|
||||||
|
pub struct VersionInfo {
|
||||||
|
pub service: String,
|
||||||
|
pub version: String,
|
||||||
|
pub compiler_version: String,
|
||||||
|
pub build_time: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 健康状态
|
||||||
|
#[derive(Debug, Serialize)]
|
||||||
|
pub struct HealthStatus {
|
||||||
|
pub status: String,
|
||||||
|
pub uptime_seconds: u64,
|
||||||
|
}
|
||||||
|
|
||||||
|
// ============================================================
|
||||||
|
// 全局状态
|
||||||
|
// ============================================================
|
||||||
|
|
||||||
|
pub struct AppState {
|
||||||
|
pub start_time: Instant,
|
||||||
|
}
|
||||||
|
|
||||||
|
// ============================================================
|
||||||
|
// API 处理函数
|
||||||
|
// ============================================================
|
||||||
|
|
||||||
|
/// POST /api/v1/compile - 编译 CNNL 源代码
|
||||||
|
async fn handle_compile(
|
||||||
|
state: web::Data<AppState>,
|
||||||
|
req: web::Json<CompileRequest>,
|
||||||
|
) -> impl Responder {
|
||||||
|
let start = Instant::now();
|
||||||
|
info!("收到编译请求,源代码长度: {} 字节", req.source.len());
|
||||||
|
|
||||||
|
let options = cnnl_compiler::CompilerOptions {
|
||||||
|
enable_verification: req.enable_verification,
|
||||||
|
debug_info: req.debug_info,
|
||||||
|
output_dir: None,
|
||||||
|
generate_state_file: req.generate_state,
|
||||||
|
};
|
||||||
|
|
||||||
|
match cnnl_compiler::compile(&req.source, options) {
|
||||||
|
Ok(result) => {
|
||||||
|
let bytecode_hex = hex::encode(&result.bytecode);
|
||||||
|
let state_json = result.state_json.as_ref().and_then(|s| {
|
||||||
|
serde_json::from_str(s).ok()
|
||||||
|
});
|
||||||
|
let clauses: Vec<ClauseSummary> = result.ast.clauses.iter().map(|c| ClauseSummary {
|
||||||
|
id: c.id.clone(),
|
||||||
|
level: c.level.to_string(),
|
||||||
|
title: c.title.clone(),
|
||||||
|
parameter_count: c.parameters.len(),
|
||||||
|
predicate_count: c.predicates.len(),
|
||||||
|
obligation_count: c.obligations.len(),
|
||||||
|
}).collect();
|
||||||
|
let clause_count = clauses.len();
|
||||||
|
let compile_result = CompileResult {
|
||||||
|
bytecode_hex,
|
||||||
|
bytecode_size: result.bytecode.len(),
|
||||||
|
state_json,
|
||||||
|
clause_count,
|
||||||
|
clauses,
|
||||||
|
};
|
||||||
|
let duration_ms = start.elapsed().as_millis() as u64;
|
||||||
|
info!("编译成功,字节码大小: {} 字节,耗时: {}ms", compile_result.bytecode_size, duration_ms);
|
||||||
|
HttpResponse::Ok().json(ApiResponse::ok(compile_result, duration_ms))
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
let duration_ms = start.elapsed().as_millis() as u64;
|
||||||
|
warn!("编译失败: {}", e);
|
||||||
|
HttpResponse::BadRequest().json(ApiResponse::<serde_json::Value>::err(
|
||||||
|
format!("{}", e),
|
||||||
|
duration_ms,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// POST /api/v1/parse - 解析 CNNL 源代码
|
||||||
|
async fn handle_parse(
|
||||||
|
_state: web::Data<AppState>,
|
||||||
|
req: web::Json<ParseRequest>,
|
||||||
|
) -> impl Responder {
|
||||||
|
let start = Instant::now();
|
||||||
|
info!("收到解析请求,源代码长度: {} 字节", req.source.len());
|
||||||
|
|
||||||
|
match cnnl_compiler::Parser::new(&req.source) {
|
||||||
|
Ok(mut parser) => {
|
||||||
|
match parser.parse() {
|
||||||
|
Ok(ast) => {
|
||||||
|
let clauses: Vec<ClauseDetail> = ast.clauses.iter().map(|c| ClauseDetail {
|
||||||
|
id: c.id.clone(),
|
||||||
|
level: c.level.to_string(),
|
||||||
|
title: c.title.clone(),
|
||||||
|
name: c.name.clone(),
|
||||||
|
version: c.version.clone(),
|
||||||
|
description: c.description.clone(),
|
||||||
|
depends_on: c.depends_on.clone(),
|
||||||
|
parameters: c.parameters.iter().map(|p| ParameterDetail {
|
||||||
|
name: p.name.clone(),
|
||||||
|
type_name: p.ty.to_string(),
|
||||||
|
value: format!("{}", p.value),
|
||||||
|
description: p.description.clone(),
|
||||||
|
}).collect(),
|
||||||
|
predicates: c.predicates.iter().map(|pred| PredicateDetail {
|
||||||
|
name: pred.name.clone(),
|
||||||
|
params: pred.params.iter().map(|(n, t)| (n.clone(), t.to_string())).collect(),
|
||||||
|
return_type: pred.return_type.to_string(),
|
||||||
|
}).collect(),
|
||||||
|
obligations: c.obligations.iter().map(|o| ObligationDetail {
|
||||||
|
name: o.name.clone(),
|
||||||
|
frequency: format!("{:?}", o.frequency),
|
||||||
|
enforcer: o.enforcer.clone(),
|
||||||
|
penalty: o.penalty.clone(),
|
||||||
|
description: o.description.clone(),
|
||||||
|
}).collect(),
|
||||||
|
}).collect();
|
||||||
|
let test_count = ast.tests.len();
|
||||||
|
let clause_count = clauses.len();
|
||||||
|
let parse_result = ParseResult {
|
||||||
|
clause_count,
|
||||||
|
test_count,
|
||||||
|
clauses,
|
||||||
|
};
|
||||||
|
let duration_ms = start.elapsed().as_millis() as u64;
|
||||||
|
info!("解析成功,条款数: {},测试块数: {},耗时: {}ms", clause_count, test_count, duration_ms);
|
||||||
|
HttpResponse::Ok().json(ApiResponse::ok(parse_result, duration_ms))
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
let duration_ms = start.elapsed().as_millis() as u64;
|
||||||
|
warn!("解析失败: {}", e);
|
||||||
|
HttpResponse::BadRequest().json(ApiResponse::<serde_json::Value>::err(
|
||||||
|
format!("{}", e),
|
||||||
|
duration_ms,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
let duration_ms = start.elapsed().as_millis() as u64;
|
||||||
|
warn!("词法分析失败: {}", e);
|
||||||
|
HttpResponse::BadRequest().json(ApiResponse::<serde_json::Value>::err(
|
||||||
|
format!("{}", e),
|
||||||
|
duration_ms,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// POST /api/v1/validate - 验证 CNNL 语法
|
||||||
|
async fn handle_validate(
|
||||||
|
_state: web::Data<AppState>,
|
||||||
|
req: web::Json<ValidateRequest>,
|
||||||
|
) -> impl Responder {
|
||||||
|
let start = Instant::now();
|
||||||
|
info!("收到验证请求,源代码长度: {} 字节", req.source.len());
|
||||||
|
|
||||||
|
let mut errors = Vec::new();
|
||||||
|
let warnings = Vec::new();
|
||||||
|
let mut clause_count = 0;
|
||||||
|
|
||||||
|
match cnnl_compiler::Parser::new(&req.source) {
|
||||||
|
Ok(mut parser) => {
|
||||||
|
match parser.parse() {
|
||||||
|
Ok(ast) => {
|
||||||
|
clause_count = ast.clauses.len();
|
||||||
|
// 语义分析
|
||||||
|
let mut analyzer = cnnl_compiler::semantic::SemanticAnalyzer::new();
|
||||||
|
if let Err(e) = analyzer.analyze(&ast) {
|
||||||
|
errors.push(format!("语义错误: {}", e));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
errors.push(format!("语法错误: {}", e));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
errors.push(format!("词法错误: {}", e));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let valid = errors.is_empty();
|
||||||
|
let validate_result = ValidateResult {
|
||||||
|
valid,
|
||||||
|
errors,
|
||||||
|
warnings,
|
||||||
|
clause_count,
|
||||||
|
};
|
||||||
|
let duration_ms = start.elapsed().as_millis() as u64;
|
||||||
|
info!("验证完成,有效: {},耗时: {}ms", valid, duration_ms);
|
||||||
|
HttpResponse::Ok().json(ApiResponse::ok(validate_result, duration_ms))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// GET /api/v1/health - 健康检查
|
||||||
|
async fn handle_health(state: web::Data<AppState>) -> impl Responder {
|
||||||
|
let uptime_seconds = state.start_time.elapsed().as_secs();
|
||||||
|
let health = HealthStatus {
|
||||||
|
status: "ok".to_string(),
|
||||||
|
uptime_seconds,
|
||||||
|
};
|
||||||
|
HttpResponse::Ok().json(ApiResponse::ok(health, 0))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// GET /api/v1/version - 版本信息
|
||||||
|
async fn handle_version(_state: web::Data<AppState>) -> impl Responder {
|
||||||
|
let version = VersionInfo {
|
||||||
|
service: "nac-cnnl-service".to_string(),
|
||||||
|
version: env!("CARGO_PKG_VERSION").to_string(),
|
||||||
|
compiler_version: "0.1.0".to_string(),
|
||||||
|
build_time: env!("CARGO_PKG_VERSION").to_string(),
|
||||||
|
};
|
||||||
|
HttpResponse::Ok().json(ApiResponse::ok(version, 0))
|
||||||
|
}
|
||||||
|
|
||||||
|
// ============================================================
|
||||||
|
// 主函数
|
||||||
|
// ============================================================
|
||||||
|
|
||||||
|
#[actix_web::main]
|
||||||
|
async fn main() -> std::io::Result<()> {
|
||||||
|
env_logger::init_from_env(env_logger::Env::default().default_filter_or("info"));
|
||||||
|
|
||||||
|
let host = std::env::var("CNNL_HOST").unwrap_or_else(|_| "0.0.0.0".to_string());
|
||||||
|
let port: u16 = std::env::var("CNNL_PORT")
|
||||||
|
.unwrap_or_else(|_| "8080".to_string())
|
||||||
|
.parse()
|
||||||
|
.unwrap_or(8080);
|
||||||
|
|
||||||
|
info!("NAC CNNL HTTP 服务启动中...");
|
||||||
|
info!("监听地址: {}:{}", host, port);
|
||||||
|
|
||||||
|
let app_state = web::Data::new(AppState {
|
||||||
|
start_time: Instant::now(),
|
||||||
|
});
|
||||||
|
|
||||||
|
HttpServer::new(move || {
|
||||||
|
App::new()
|
||||||
|
.app_data(app_state.clone())
|
||||||
|
.app_data(web::JsonConfig::default().limit(1024 * 1024)) // 1MB 请求体限制
|
||||||
|
// API 路由
|
||||||
|
.route("/api/v1/compile", web::post().to(handle_compile))
|
||||||
|
.route("/api/v1/parse", web::post().to(handle_parse))
|
||||||
|
.route("/api/v1/validate", web::post().to(handle_validate))
|
||||||
|
.route("/api/v1/health", web::get().to(handle_health))
|
||||||
|
.route("/api/v1/version", web::get().to(handle_version))
|
||||||
|
})
|
||||||
|
.bind(format!("{}:{}", host, port))?
|
||||||
|
.run()
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,167 @@
|
||||||
|
# NAC 公链多服务器体系架构规划
|
||||||
|
|
||||||
|
**文档编号**:ARCH-001
|
||||||
|
**版本**:v1.0
|
||||||
|
**日期**:2026-02-28
|
||||||
|
**状态**:规划草案
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 一、架构原则
|
||||||
|
|
||||||
|
NAC 公链的多服务器体系建立在以下核心原则之上:
|
||||||
|
|
||||||
|
**参与即是共识**。区块链的本质不是集中式服务,而是每个服务节点独立运行、独立验证、独立扩展。当前将所有服务集中在一台或两台服务器上,违背了这一原则,也带来了单点故障风险。未来的目标是将每一类服务部署到独立的服务器,通过 NRPC 4.0 协议通信,形成真正的分布式服务网络。
|
||||||
|
|
||||||
|
**服务边界清晰**。每台服务器只负责一类核心职责,不承担其他服务的运行压力。这既符合微服务架构的最佳实践,也符合区块链节点独立性的要求。
|
||||||
|
|
||||||
|
**数据主权分离**。用户数据(注册系统)、链上数据(CBPP 节点)、合规数据(AI 知识引擎)、交易数据(交易所)各自存储在独立的数据库服务器上,互不干扰,互不依赖。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 二、目标多服务器体系(七层架构)
|
||||||
|
|
||||||
|
### 第一层:链核心层(Chain Core)
|
||||||
|
|
||||||
|
这是整个体系的基础,负责区块生产、共识和链上状态维护。
|
||||||
|
|
||||||
|
| 服务器角色 | 部署服务 | 域名 | 当前状态 |
|
||||||
|
|-----------|---------|------|---------|
|
||||||
|
| **主链节点服务器** | CBPP 节点(nac-cbpp-node)、NRPC 4.0 网关 | `rpc.newassetchain.io` | 已部署(备份服务器) |
|
||||||
|
| **备用链节点服务器** | CBPP 节点副本(热备) | — | 待规划 |
|
||||||
|
|
||||||
|
**独立理由**:CBPP 节点是整个公链的心脏,必须独立运行,不能与任何应用层服务共享服务器资源。链节点的 CPU 和 I/O 需求与应用服务完全不同,混合部署会导致出块延迟。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 第二层:身份层(Identity Layer)
|
||||||
|
|
||||||
|
负责用户注册、KYC 验证、DID 身份管理。
|
||||||
|
|
||||||
|
| 服务器角色 | 部署服务 | 域名 | 当前状态 |
|
||||||
|
|-----------|---------|------|---------|
|
||||||
|
| **注册系统服务器** | nac-id-system(Laravel/PHP)、MySQL(nac_id 数据库) | `id.newassetchain.io` | 已部署(备份服务器),**待迁出** |
|
||||||
|
| **认证网关** | nac-auth-service(端口 8081) | — | 已部署(备份服务器),随注册系统迁移 |
|
||||||
|
|
||||||
|
**独立理由**:注册系统存储用户的最敏感数据(身份证、KYC 文件),必须部署在专用服务器上,配备独立的数据库备份策略和更严格的访问控制。注册系统与链节点的耦合应通过 NRPC 4.0 协议完成,而非直接数据库访问。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 第三层:AI 智能层(AI Intelligence Layer)
|
||||||
|
|
||||||
|
负责合规分析、资产估值、审批辅助、知识库管理。
|
||||||
|
|
||||||
|
| 服务器角色 | 部署服务 | 域名 | 当前状态 |
|
||||||
|
|-----------|---------|------|---------|
|
||||||
|
| **AI 知识引擎服务器** | nac-admin(Node.js)、MongoDB(nac_knowledge_engine) | `admin.newassetchain.io` | 已部署(备份服务器),**待迁出** |
|
||||||
|
| **AI 估值服务器**(未来) | nac-ai-valuation(Rust)、xtzh-ai(Transformer 模型) | `aivl.newassetchain.io` | 待规划 |
|
||||||
|
|
||||||
|
**独立理由**:AI 推理(尤其是 Transformer 模型)对 GPU/CPU 的需求远高于普通 Web 服务。将 AI 服务独立到专用服务器,可以在不影响其他服务的情况下进行模型升级和算力扩展。MongoDB 知识库(35 条规则,持续增长)也需要独立的存储和索引优化。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 第四层:资产上链层(Asset Onboarding Layer)
|
||||||
|
|
||||||
|
负责 RWA 资产的上链流程、合规验证、DNA 生成。
|
||||||
|
|
||||||
|
| 服务器角色 | 部署服务 | 域名 | 当前状态 |
|
||||||
|
|-----------|---------|------|---------|
|
||||||
|
| **资产上链服务器** | nac-onboarding(Python FastAPI)、CIB 服务 | `onboarding.newassetchain.io`、`cib.newassetchain.io` | 已部署(备份服务器),待迁出 |
|
||||||
|
|
||||||
|
**独立理由**:资产上链是 NAC 的核心业务流程,涉及文件上传、AI 合规验证、链上交易提交等多个步骤,处理时间长、资源消耗大,必须独立运行以保证服务质量。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 第五层:数据服务层(Data Service Layer)
|
||||||
|
|
||||||
|
负责链上数据的聚合、查询、可视化。
|
||||||
|
|
||||||
|
| 服务器角色 | 部署服务 | 域名 | 当前状态 |
|
||||||
|
|-----------|---------|------|---------|
|
||||||
|
| **区块浏览器服务器** | nac-quantum-browser(PHP WorkerMan + WebSocket) | `explorer.newassetchain.io` | 已部署(备份服务器),待迁出 |
|
||||||
|
| **数据服务器** | NAC Lens(Node.js)、NAC API Server(Rust) | `lens.newassetchain.io`、`api.newassetchain.io` | 已部署(备份服务器),待迁出 |
|
||||||
|
|
||||||
|
**独立理由**:区块浏览器和数据服务是面向公众的高流量服务,需要独立的 CDN 加速和负载均衡,不能与链节点共享带宽。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 第六层:金融服务层(Financial Service Layer)
|
||||||
|
|
||||||
|
负责 XTZH 稳定币、钱包、交易所。
|
||||||
|
|
||||||
|
| 服务器角色 | 部署服务 | 域名 | 当前状态 |
|
||||||
|
|-----------|---------|------|---------|
|
||||||
|
| **钱包与稳定币服务器** | nac-wallet-service(Rust)、xtzh-pricing-service(Python) | `wallet.newassetchain.io` | 已部署(备份服务器),待迁出 |
|
||||||
|
| **交易所服务器** | nac-exchange-service(Rust) | `exchange.newassetchain.io`、`exchange-admin.newassetchain.io` | 已部署(备份服务器),待迁出 |
|
||||||
|
|
||||||
|
**独立理由**:金融服务涉及资金安全,必须部署在符合金融级安全标准的服务器上,配备独立的 HSM(硬件安全模块)和审计日志系统。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 第七层:展示与门户层(Portal Layer)
|
||||||
|
|
||||||
|
负责公开网站、ICO 门户、营销页面。
|
||||||
|
|
||||||
|
| 服务器角色 | 部署服务 | 域名 | 当前状态 |
|
||||||
|
|-----------|---------|------|---------|
|
||||||
|
| **主站服务器** | www.newassetchain.io(Node.js) | `www.newassetchain.io` | 已部署(主服务器 103.43.188.244) |
|
||||||
|
| **ICO 门户服务器** | NILA Token 购买页面(静态 HTML) | `ico.newassetchain.io`、`trc-ico.newassetchain.io` | 已部署(主服务器) |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 三、服务间通信规范
|
||||||
|
|
||||||
|
所有服务器之间的通信必须通过 **NRPC 4.0 协议**,禁止直接数据库访问跨服务器调用。
|
||||||
|
|
||||||
|
| 通信场景 | 发起方 | 接收方 | 协议 | 说明 |
|
||||||
|
|---------|--------|--------|------|------|
|
||||||
|
| 注册用户上链 | 注册系统服务器 | 主链节点服务器 | NRPC 4.0 `nac_sendTx` | 工单 REG-001 |
|
||||||
|
| AI 合规查询链上状态 | AI 知识引擎服务器 | 主链节点服务器 | NRPC 4.0 `nac_getBlock` | — |
|
||||||
|
| 资产上链提交交易 | 资产上链服务器 | 主链节点服务器 | NRPC 4.0 `nac_sendTx` | — |
|
||||||
|
| 区块浏览器读取数据 | 数据服务层 | 主链节点服务器 | NRPC 4.0 `nac_getBlock` | — |
|
||||||
|
| 钱包发送交易 | 金融服务层 | 主链节点服务器 | NRPC 4.0 `nac_sendTx` | — |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 四、迁移优先级与路线图
|
||||||
|
|
||||||
|
迁移应按照对链核心的影响程度从低到高进行,优先迁移独立性最强的服务。
|
||||||
|
|
||||||
|
| 优先级 | 服务 | 迁移难度 | 预计工期 | 前置条件 |
|
||||||
|
|--------|------|---------|---------|---------|
|
||||||
|
| **P1(立即)** | AI 知识引擎(nac-admin) | 低 | 1 天 | 新服务器 + MongoDB + MySQL |
|
||||||
|
| **P2(近期)** | 注册系统(nac-id-system) | 中 | 2 天 | 新服务器 + MySQL + 工单 REG-001 完成 |
|
||||||
|
| **P3(中期)** | 区块浏览器 + 数据服务 | 中 | 3 天 | 新服务器 + NRPC 4.0 网关稳定 |
|
||||||
|
| **P4(中期)** | 资产上链服务 | 高 | 5 天 | 新服务器 + AI 知识引擎迁移完成 |
|
||||||
|
| **P5(远期)** | 金融服务(钱包/交易所) | 高 | 7 天 | 金融级安全审计完成 |
|
||||||
|
| **P6(远期)** | 链节点独立扩展(多节点) | 极高 | 持续 | CSNP 网络协议完善 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 五、数据库独立化策略
|
||||||
|
|
||||||
|
当前所有数据库(MySQL + MongoDB)集中在备份服务器上,这是单点故障的最大风险。未来应建立独立的数据服务器:
|
||||||
|
|
||||||
|
| 数据库 | 当前位置 | 目标位置 | 数据内容 |
|
||||||
|
|--------|---------|---------|---------|
|
||||||
|
| `nac_id`(MySQL) | 备份服务器 | 独立数据服务器 | 用户账号、KYC 信息 |
|
||||||
|
| `nac_knowledge_engine`(MongoDB) | 备份服务器 | AI 知识引擎服务器本地 | 35 条合规规则、对话历史 |
|
||||||
|
| `gnacs_db`(MongoDB) | 备份服务器 | 独立数据服务器 | GNACS 编码、资产分类 |
|
||||||
|
| `nac_onboarding`(MongoDB) | 备份服务器 | 资产上链服务器本地 | 上链申请、合规记录 |
|
||||||
|
| 链上状态(RocksDB) | 备份服务器 | 主链节点服务器本地 | 区块数据、账户状态 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 六、与区块链去中心化原则的对应关系
|
||||||
|
|
||||||
|
| 区块链原则 | NAC 多服务器体系的实现方式 |
|
||||||
|
|-----------|------------------------|
|
||||||
|
| **节点独立性** | 每台服务器独立运行,单台服务器故障不影响整体网络 |
|
||||||
|
| **数据主权** | 每类数据存储在对应职责的服务器上,无跨服务器直接访问 |
|
||||||
|
| **协议通信** | 所有服务间通信通过 NRPC 4.0 协议,而非内网直连 |
|
||||||
|
| **参与即是共识** | 注册系统通过 `nac_sendTx` 将每次注册写入链上,每台服务器的运行本身就是对网络的贡献 |
|
||||||
|
| **可扩展性** | 每层服务可独立横向扩展,无需整体重构 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
*文档归档路径:`nac-docs-center/docs/architecture/ARCH-001-multi-server-plan.md`*
|
||||||
Loading…
Reference in New Issue