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:
parent
965a061463
commit
dfe2a85d69
|
|
@ -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");
|
||||
|
|
|
|||
|
|
@ -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(),
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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![],
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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(),
|
||||
|
|
|
|||
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
|
||||
}
|
||||
Loading…
Reference in New Issue