fix(cnnl): 修复编译器字段名并添加新字段支持,实现 cnnl-service HTTP 服务

修复内容:
1. ast.rs: 恢复原始字段名 ty: Type(而非 type_: TypeAnnotation)
   - 添加 name/version/description 字段到 Clause
   - 添加 description 字段到 Obligation
   - 添加 tests 字段到 Program
   - 添加 Expression::Raw 变体
   - 添加 ObligationFrequency::Display 实现
   - 添加 Literal::Display 实现

2. parser/mod.rs: 完全重写,基于原始版本添加新字段支持
   - 支持 name:/version:/description: 字段解析
   - 支持 test "..." { assert ... } 测试块解析
   - 支持 obligation { description: ... } 字段

3. 修复所有模块的编译错误:
   - codegen/bytecode_generator.rs
   - semantic/type_checker.rs
   - semantic/scope_resolver.rs
   - semantic/obligation_validator.rs
   - verification/constraint_generator.rs

4. 新增 cnnl-service HTTP 服务:
   - POST /api/v1/compile  - 编译 CNNL 源代码
   - POST /api/v1/parse    - 解析 CNNL 源代码(返回 AST)
   - POST /api/v1/validate - 验证 CNNL 语法
   - GET  /api/v1/health   - 健康检查
   - GET  /api/v1/version  - 版本信息
   - 监听端口 8765,已部署为 systemd 服务

测试结果:26/26 通过
关联 Issues: #64 #65
This commit is contained in:
NAC Core Team 2026-02-28 07:52:27 +08:00
parent 965a061463
commit dfe2a85d69
10 changed files with 3222 additions and 206 deletions

View File

@ -65,6 +65,7 @@ impl BytecodeGenerator {
crate::parser::Type::U32 => 0x02,
crate::parser::Type::F64 => 0x03,
crate::parser::Type::String => 0x04,
crate::parser::Type::U128 => 0x05,
};
self.emit_byte(type_byte);
}
@ -117,7 +118,7 @@ impl BytecodeGenerator {
}
crate::parser::Literal::Int(i) => {
self.emit_byte(0x11); // PUSH_INT
self.emit_u64(*i);
self.emit_u64(*i as u64);
}
crate::parser::Literal::Float(f) => {
self.emit_byte(0x12); // PUSH_FLOAT
@ -198,6 +199,9 @@ impl BytecodeGenerator {
self.emit_byte(0x10); // PUSH_BOOL
self.emit_byte(1); // true
}
Expression::Raw(_) => {
self.emit_byte(0x00);
}
}
Ok(())
@ -251,11 +255,15 @@ mod tests {
id: "TEST".to_string(),
level: ClauseLevel::Eternal,
title: "Test".to_string(),
name: None,
version: None,
description: None,
parameters: vec![],
predicates: vec![],
obligations: vec![],
depends_on: vec![],
}],
tests: vec![],
};
let bytecode = generator.generate_program(&program).expect("Bytecode generation failed");

View File

@ -1,169 +1,52 @@
//! CNNL抽象语法树AST定义
use serde::{Deserialize, Serialize};
/// CNNL抽象语法树根节点
/// 程序(顶层节点)
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct Program {
pub clauses: Vec<Clause>,
/// 测试块test "..." { assert ... }
#[serde(default)]
pub tests: Vec<TestBlock>,
}
/// 宪法条款
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct Clause {
/// 条款标识符(全大写)
pub id: String,
/// 条款层级
pub level: ClauseLevel,
/// 条款标题
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 parameters: Vec<Parameter>,
/// 谓词列表
pub predicates: Vec<Predicate>,
/// 义务列表
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)]
pub enum Type {
Bool,
U64,
U32,
F64,
String,
}
/// 字面量
#[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,
},
pub enum ClauseLevel {
/// 永恒条款(不可修改)
Eternal,
/// 战略条款(需要超级多数修改)
Strategic,
/// 战术条款(普通多数修改)
Tactical,
}
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 {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
match self {
Type::Bool => write!(f, "bool"),
Type::U64 => write!(f, "u64"),
Type::U32 => write!(f, "u32"),
Type::U128 => write!(f, "u128"),
Type::F64 => write!(f, "f64"),
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 {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
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 {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
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)]
mod tests {
use super::*;
@ -227,6 +290,9 @@ mod tests {
id: "XTZH_GOLD_COVERAGE".to_string(),
level: ClauseLevel::Eternal,
title: "黄金储备覆盖率底线".to_string(),
name: None,
version: None,
description: None,
depends_on: vec![],
parameters: vec![Parameter {
name: "XTZH_GOLD_COVERAGE_MIN".to_string(),

View File

@ -1,5 +1,4 @@
pub mod ast;
pub use ast::*;
use crate::lexer::{Lexer, Token};
use thiserror::Error;
@ -9,13 +8,10 @@ use thiserror::Error;
pub enum ParserError {
#[error("Unexpected token: expected {expected}, found {found}")]
UnexpectedToken { expected: String, found: String },
#[error("Unexpected end of file")]
UnexpectedEof,
#[error("Invalid syntax: {message}")]
InvalidSyntax { message: String },
#[error("Lexer error: {0}")]
LexerError(#[from] crate::lexer::LexerError),
}
@ -33,7 +29,6 @@ impl<'source> Parser<'source> {
let mut lexer = Lexer::new(source);
let current = lexer.next().transpose()?;
let peek = lexer.next().transpose()?;
Ok(Self {
lexer,
current,
@ -44,12 +39,23 @@ impl<'source> Parser<'source> {
/// 解析整个程序
pub fn parse(&mut self) -> Result<Program, ParserError> {
let mut clauses = Vec::new();
let mut tests = Vec::new();
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 })
Ok(Program { clauses, tests })
}
/// 解析条款
@ -61,6 +67,9 @@ impl<'source> Parser<'source> {
let mut level = ClauseLevel::Tactical;
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 parameters = Vec::new();
let mut predicates = Vec::new();
@ -79,6 +88,25 @@ impl<'source> Parser<'source> {
self.expect(Token::Colon)?;
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) => {
self.advance();
self.expect(Token::Colon)?;
@ -100,13 +128,15 @@ impl<'source> Parser<'source> {
}
}
}
self.expect(Token::RBrace)?;
Ok(Clause {
id,
level,
title,
name,
version,
description,
depends_on,
parameters,
predicates,
@ -145,7 +175,10 @@ impl<'source> Parser<'source> {
let ty = self.parse_type()?;
self.expect(Token::Assign)?;
let value = self.parse_literal()?;
// 可选分号
if self.check(&Token::Semicolon) {
self.advance();
}
Ok(Parameter {
name,
ty,
@ -160,29 +193,23 @@ impl<'source> Parser<'source> {
self.expect(Token::Predicate)?;
let name = self.expect_ident()?;
self.expect(Token::LParen)?;
let mut params = Vec::new();
while !self.check(&Token::RParen) {
let param_name = self.expect_ident()?;
self.expect(Token::Colon)?;
let param_type = self.parse_type()?;
params.push((param_name, param_type));
if self.check(&Token::Comma) {
self.advance();
}
}
self.expect(Token::RParen)?;
self.expect(Token::Arrow)?;
let return_type = self.parse_type()?;
self.expect(Token::LBrace)?;
// 简化:只解析单个返回表达式
let body = self.parse_expression()?;
self.expect(Token::RBrace)?;
Ok(Predicate {
name,
params,
@ -201,6 +228,7 @@ impl<'source> Parser<'source> {
let mut frequency = ObligationFrequency::OnDemand;
let mut enforcer = String::new();
let mut penalty = String::new();
let mut description: Option<String> = None;
while !self.check(&Token::RBrace) {
match &self.current {
@ -219,18 +247,21 @@ impl<'source> Parser<'source> {
self.expect(Token::Colon)?;
penalty = self.expect_ident()?;
}
Some(Token::Description) => {
self.advance();
self.expect(Token::Colon)?;
description = Some(self.expect_string_literal()?);
}
_ => {
return Err(ParserError::InvalidSyntax {
message: format!("Unexpected token in obligation: {:?}", self.current),
});
}
}
if self.check(&Token::Comma) {
self.advance();
}
}
self.expect(Token::RBrace)?;
Ok(Obligation {
@ -238,6 +269,7 @@ impl<'source> Parser<'source> {
frequency,
enforcer,
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> {
self.parse_comparison()
@ -271,7 +347,6 @@ impl<'source> Parser<'source> {
/// 解析比较表达式
fn parse_comparison(&mut self) -> Result<Expression, ParserError> {
let mut left = self.parse_term()?;
while let Some(token) = &self.current {
let op = match token {
Token::Eq => BinaryOp::Eq,
@ -282,7 +357,6 @@ impl<'source> Parser<'source> {
Token::Ge => BinaryOp::Ge,
_ => break,
};
self.advance();
let right = self.parse_term()?;
left = Expression::Binary {
@ -291,21 +365,18 @@ impl<'source> Parser<'source> {
right: Box::new(right),
};
}
Ok(left)
}
/// 解析项
fn parse_term(&mut self) -> Result<Expression, ParserError> {
let mut left = self.parse_factor()?;
while let Some(token) = &self.current {
let op = match token {
Token::Plus => BinaryOp::Add,
Token::Minus => BinaryOp::Sub,
_ => break,
};
self.advance();
let right = self.parse_factor()?;
left = Expression::Binary {
@ -314,60 +385,80 @@ impl<'source> Parser<'source> {
right: Box::new(right),
};
}
Ok(left)
}
/// 解析因子
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 {
let op = match token {
Token::Star => BinaryOp::Mul,
Token::Slash => BinaryOp::Div,
Token::Percent => BinaryOp::Mod,
_ => break,
};
self.advance();
let right = self.parse_primary()?;
let right = self.parse_unary()?;
left = Expression::Binary {
op,
left: Box::new(left),
right: Box::new(right),
};
}
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> {
match &self.current {
Some(Token::IntLiteral(n)) => {
let n = *n;
self.advance();
Ok(Expression::Literal(Literal::Int(n)))
Some(Token::IntLiteral(_))
| Some(Token::FloatLiteral(_))
| Some(Token::BoolLiteral(_))
| Some(Token::StringLiteral(_)) => {
let lit = self.parse_literal()?;
Ok(Expression::Literal(lit))
}
Some(Token::FloatLiteral(f)) => {
let f = *f;
self.advance();
Ok(Expression::Literal(Literal::Float(f)))
}
Some(Token::BoolLiteral(b)) => {
let b = *b;
self.advance();
Ok(Expression::Literal(Literal::Bool(b)))
}
Some(Token::StringLiteral(s)) => {
let s = s.clone();
self.advance();
Ok(Expression::Literal(Literal::String(s)))
}
Some(Token::Ident(name)) | Some(Token::ConstantIdent(name)) => {
let name = name.clone();
self.advance();
Ok(Expression::Variable(name))
Some(Token::Ident(_)) | Some(Token::ConstantIdent(_)) => {
let name = self.expect_ident()?;
// 检查是否是函数调用
if self.check(&Token::LParen) {
self.advance();
let mut args = Vec::new();
while !self.check(&Token::RParen) {
args.push(self.parse_expression()?);
if self.check(&Token::Comma) {
self.advance();
}
}
self.expect(Token::RParen)?;
Ok(Expression::Call { name, args })
} else {
Ok(Expression::Variable(name))
}
}
Some(Token::LParen) => {
self.advance();
@ -397,6 +488,10 @@ impl<'source> Parser<'source> {
self.advance();
Ok(Type::U32)
}
Some(Token::U128) => {
self.advance();
Ok(Type::U128)
}
Some(Token::F64) => {
self.advance();
Ok(Type::F64)
@ -446,15 +541,12 @@ impl<'source> Parser<'source> {
fn parse_string_array(&mut self) -> Result<Vec<String>, ParserError> {
self.expect(Token::LBracket)?;
let mut strings = Vec::new();
while !self.check(&Token::RBracket) {
strings.push(self.expect_string_literal()?);
if self.check(&Token::Comma) {
self.advance();
}
}
self.expect(Token::RBracket)?;
Ok(strings)
}
@ -546,16 +638,41 @@ mod tests {
parameter XTZH_GOLD_COVERAGE_MIN: f64 = 1.25
}
"#;
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, "XTZH_GOLD_COVERAGE");
assert_eq!(program.clauses[0].level, ClauseLevel::Eternal);
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]
fn test_parse_predicate() {
let source = r#"
@ -567,11 +684,24 @@ mod tests {
}
}
"#;
let mut parser = Parser::new(source).expect("Parser creation failed");
let program = parser.parse().expect("Parse failed");
assert_eq!(program.clauses[0].predicates.len(), 1);
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);
}
}

View File

@ -124,6 +124,9 @@ mod tests {
id: "TEST".to_string(),
level: ClauseLevel::Eternal,
title: "Test".to_string(),
name: None,
version: None,
description: None,
parameters: vec![],
predicates: vec![],
obligations: vec![Obligation {
@ -131,6 +134,7 @@ mod tests {
frequency: crate::parser::ObligationFrequency::Continuous,
enforcer: "ai_system".to_string(),
penalty: "suspension".to_string(),
description: None,
}],
depends_on: vec![],
};
@ -146,6 +150,9 @@ mod tests {
id: "TEST".to_string(),
level: ClauseLevel::Eternal,
title: "Test".to_string(),
name: None,
version: None,
description: None,
parameters: vec![],
predicates: vec![],
obligations: vec![Obligation {
@ -153,6 +160,7 @@ mod tests {
frequency: crate::parser::ObligationFrequency::Continuous,
enforcer: "".to_string(),
penalty: "suspension".to_string(),
description: None,
}],
depends_on: vec![],
};
@ -168,6 +176,9 @@ mod tests {
id: "TEST".to_string(),
level: ClauseLevel::Eternal,
title: "Test".to_string(),
name: None,
version: None,
description: None,
parameters: vec![],
predicates: vec![],
obligations: vec![Obligation {
@ -175,6 +186,7 @@ mod tests {
frequency: crate::parser::ObligationFrequency::Continuous,
enforcer: "manual".to_string(),
penalty: "suspension".to_string(),
description: None,
}],
depends_on: vec![],
};

View File

@ -161,6 +161,7 @@ impl ScopeResolver {
Expression::Literal(_) => {
// 字面量不需要解析
}
crate::parser::Expression::Raw(_) => {}
}
}
}
@ -255,6 +256,9 @@ mod tests {
id: "TEST".to_string(),
level: ClauseLevel::Eternal,
title: "Test".to_string(),
name: None,
version: None,
description: None,
parameters: vec![Parameter {
name: "x".to_string(),
ty: Type::U64,

View File

@ -132,6 +132,7 @@ impl TypeChecker {
Expression::Call { name: _, args: _ } => Type::Bool,
Expression::If { .. } => Type::Bool,
Expression::Block(_) => Type::Bool,
crate::parser::Expression::Raw(_) => Type::Bool,
}
}
}
@ -155,6 +156,9 @@ mod tests {
id: "TEST".to_string(),
level: crate::parser::ClauseLevel::Eternal,
title: "Test".to_string(),
name: None,
version: None,
description: None,
parameters: vec![Parameter {
name: "x".to_string(),
ty: Type::U64,
@ -182,6 +186,9 @@ mod tests {
id: "TEST".to_string(),
level: crate::parser::ClauseLevel::Eternal,
title: "Test".to_string(),
name: None,
version: None,
description: None,
parameters: vec![Parameter {
name: "x".to_string(),
ty: Type::U64,

View File

@ -95,6 +95,7 @@ impl ConstraintGenerator {
Expression::Block(_) => {
"true".to_string() // 简化处理
}
Expression::Raw(s) => s.clone(),
}
}
@ -137,6 +138,9 @@ mod tests {
id: "TEST".to_string(),
level: ClauseLevel::Eternal,
title: "Test".to_string(),
name: None,
version: None,
description: None,
parameters: vec![],
predicates: vec![Predicate {
name: "test_pred".to_string(),

2314
cnnl-service/Cargo.lock generated Normal file

File diff suppressed because it is too large Load Diff

39
cnnl-service/Cargo.toml Normal file
View File

@ -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

432
cnnl-service/src/main.rs Normal file
View File

@ -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
}