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::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,56 +1,96 @@
|
||||||
|
//! 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)]
|
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
pub enum ClauseLevel {
|
pub enum ClauseLevel {
|
||||||
Eternal, // 永恒条款
|
/// 永恒条款(不可修改)
|
||||||
Strategic, // 战略条款
|
Eternal,
|
||||||
Tactical, // 战术条款
|
/// 战略条款(需要超级多数修改)
|
||||||
|
Strategic,
|
||||||
|
/// 战术条款(普通多数修改)
|
||||||
|
Tactical,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 宪法参数
|
impl std::fmt::Display for ClauseLevel {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||||
|
match self {
|
||||||
|
ClauseLevel::Eternal => write!(f, "eternal"),
|
||||||
|
ClauseLevel::Strategic => write!(f, "strategic"),
|
||||||
|
ClauseLevel::Tactical => write!(f, "tactical"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 参数
|
||||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||||
pub struct Parameter {
|
pub struct Parameter {
|
||||||
pub name: String,
|
pub name: String,
|
||||||
|
/// 参数类型
|
||||||
pub ty: Type,
|
pub ty: Type,
|
||||||
pub value: Literal,
|
pub value: Literal,
|
||||||
|
/// 参数描述(可选)
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
pub description: Option<String>,
|
pub description: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 宪法谓词
|
/// 谓词
|
||||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||||
pub struct Predicate {
|
pub struct Predicate {
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub params: Vec<(String, Type)>,
|
pub params: Vec<(String, Type)>,
|
||||||
pub return_type: Type,
|
pub return_type: Type,
|
||||||
|
/// 函数体(AST 表达式)
|
||||||
pub body: Expression,
|
pub body: Expression,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 宪法义务
|
/// 义务
|
||||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||||
pub struct Obligation {
|
pub struct Obligation {
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub frequency: ObligationFrequency,
|
pub frequency: ObligationFrequency,
|
||||||
pub enforcer: String,
|
pub enforcer: String,
|
||||||
pub penalty: String,
|
pub penalty: String,
|
||||||
|
/// 义务描述(可选)
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub description: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 义务频率
|
/// 义务频率
|
||||||
|
|
@ -61,25 +101,60 @@ pub enum ObligationFrequency {
|
||||||
OnDemand, // 按需
|
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)]
|
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
pub enum Type {
|
pub enum Type {
|
||||||
Bool,
|
Bool,
|
||||||
U64,
|
U64,
|
||||||
U32,
|
U32,
|
||||||
|
U128,
|
||||||
F64,
|
F64,
|
||||||
String,
|
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)]
|
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||||
pub enum Literal {
|
pub enum Literal {
|
||||||
Bool(bool),
|
Bool(bool),
|
||||||
Int(u64),
|
Int(i64),
|
||||||
Float(f64),
|
Float(f64),
|
||||||
String(String),
|
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)]
|
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||||
pub enum Expression {
|
pub enum Expression {
|
||||||
|
|
@ -117,6 +192,9 @@ pub enum Expression {
|
||||||
|
|
||||||
// 代码块
|
// 代码块
|
||||||
Block(Vec<Statement>),
|
Block(Vec<Statement>),
|
||||||
|
|
||||||
|
// 原始字符串(解析器无法完整解析时的降级表示)
|
||||||
|
Raw(String),
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 二元运算符
|
/// 二元运算符
|
||||||
|
|
@ -142,52 +220,6 @@ pub enum BinaryOp {
|
||||||
Or,
|
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 {
|
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
|
||||||
match self {
|
|
||||||
ClauseLevel::Eternal => write!(f, "eternal"),
|
|
||||||
ClauseLevel::Strategic => write!(f, "strategic"),
|
|
||||||
ClauseLevel::Tactical => write!(f, "tactical"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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::F64 => write!(f, "f64"),
|
|
||||||
Type::String => write!(f, "string"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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() {
|
||||||
|
match &self.current {
|
||||||
|
Some(Token::Clause) => {
|
||||||
clauses.push(self.parse_clause()?);
|
clauses.push(self.parse_clause()?);
|
||||||
}
|
}
|
||||||
|
Some(Token::Test) => {
|
||||||
Ok(Program { clauses })
|
tests.push(self.parse_test_block()?);
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
return Err(ParserError::InvalidSyntax {
|
||||||
|
message: format!("Expected clause or test, found {:?}", self.current),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(Program { clauses, tests })
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 解析条款
|
/// 解析条款
|
||||||
|
|
@ -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,61 +385,81 @@ 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()?;
|
||||||
|
// 检查是否是函数调用
|
||||||
|
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.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)) => {
|
self.expect(Token::RParen)?;
|
||||||
let s = s.clone();
|
Ok(Expression::Call { name, args })
|
||||||
self.advance();
|
} else {
|
||||||
Ok(Expression::Literal(Literal::String(s)))
|
|
||||||
}
|
|
||||||
Some(Token::Ident(name)) | Some(Token::ConstantIdent(name)) => {
|
|
||||||
let name = name.clone();
|
|
||||||
self.advance();
|
|
||||||
Ok(Expression::Variable(name))
|
Ok(Expression::Variable(name))
|
||||||
}
|
}
|
||||||
|
}
|
||||||
Some(Token::LParen) => {
|
Some(Token::LParen) => {
|
||||||
self.advance();
|
self.advance();
|
||||||
let expr = self.parse_expression()?;
|
let expr = self.parse_expression()?;
|
||||||
|
|
@ -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
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue