mirror of
https://github.com/trailofbits/circomspect.git
synced 2026-01-10 06:17:54 -05:00
Initial commit.
This commit is contained in:
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
/target
|
||||
1078
Cargo.lock
generated
Normal file
1078
Cargo.lock
generated
Normal file
File diff suppressed because it is too large
Load Diff
7
Cargo.toml
Normal file
7
Cargo.toml
Normal file
@@ -0,0 +1,7 @@
|
||||
[workspace]
|
||||
members = [
|
||||
"cli",
|
||||
"parser",
|
||||
"program_analysis",
|
||||
"program_structure"
|
||||
]
|
||||
13
cli/Cargo.toml
Normal file
13
cli/Cargo.toml
Normal file
@@ -0,0 +1,13 @@
|
||||
[package]
|
||||
name = "circom-spectacles"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1.0"
|
||||
log = "0.4"
|
||||
parser = { path = "../parser" }
|
||||
pretty_env_logger = "0.4"
|
||||
program_analysis = { path = "../program_analysis" }
|
||||
program_structure = { path = "../program_structure" }
|
||||
structopt = "0.3"
|
||||
55
cli/src/main.rs
Normal file
55
cli/src/main.rs
Normal file
@@ -0,0 +1,55 @@
|
||||
use anyhow::{anyhow, Result};
|
||||
use log::info;
|
||||
use pretty_env_logger;
|
||||
use structopt::StructOpt;
|
||||
|
||||
use parser;
|
||||
use program_analysis::shadowing_analysis::ShadowingAnalysis;
|
||||
use program_structure::error_definition::Report;
|
||||
use program_structure::program_archive::ProgramArchive;
|
||||
|
||||
const CIRCOM_VERSION: &str = "2.0.3";
|
||||
|
||||
#[derive(StructOpt)]
|
||||
/// Analyze Circom programs
|
||||
struct CLI {
|
||||
/// Initial iput file
|
||||
#[structopt(name = "input")]
|
||||
input_file: String,
|
||||
|
||||
/// Output file (defaults to stdout)
|
||||
#[structopt(name = "output")]
|
||||
output_file: Option<String>,
|
||||
|
||||
/// Expected compiler version
|
||||
#[structopt(long, short)]
|
||||
compiler_version: Option<String>,
|
||||
}
|
||||
|
||||
fn parse_project(
|
||||
initial_file: &str,
|
||||
compiler_version: Option<String>,
|
||||
) -> Result<ProgramArchive> {
|
||||
let compiler_version = &compiler_version.unwrap_or(CIRCOM_VERSION.to_string());
|
||||
match parser::run_parser(initial_file.to_string(), compiler_version) {
|
||||
Result::Err((files, reports)) => {
|
||||
Report::print_reports(&reports, &files);
|
||||
Err(anyhow!("failed to parse {}", initial_file))
|
||||
}
|
||||
Result::Ok((program, warnings)) => {
|
||||
Report::print_reports(&warnings, &program.file_library);
|
||||
Ok(program)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn main() -> Result<()> {
|
||||
pretty_env_logger::init();
|
||||
let options = CLI::from_args();
|
||||
let program = parse_project(&options.input_file, options.compiler_version)?;
|
||||
|
||||
let mut analysis = ShadowingAnalysis::new();
|
||||
let reports = analysis.run(&program);
|
||||
Report::print_reports(&reports, &program.file_library);
|
||||
Ok(())
|
||||
}
|
||||
21
examples/shadowing-variable.circom
Normal file
21
examples/shadowing-variable.circom
Normal file
@@ -0,0 +1,21 @@
|
||||
pragma circom 2.0.0;
|
||||
|
||||
template ToBits(n) {
|
||||
signal input in;
|
||||
signal output out[n];
|
||||
var power = 1;
|
||||
var result = 0;
|
||||
for (var i = 0; i < n; i++) {
|
||||
// This declaration shadows the outer declaration of power.
|
||||
var power = 2;
|
||||
out[i] <-- (in >> i) & 1;
|
||||
out[i] * (out[i] - 1) === 0;
|
||||
result += out[i] * power;
|
||||
power = power + power;
|
||||
}
|
||||
// This declaration shadows the previous declaration of power.
|
||||
var power = 3;
|
||||
result === in;
|
||||
}
|
||||
|
||||
component main {public [in]} = ToBits(256);
|
||||
19
examples/unconstrained-signal.circom
Normal file
19
examples/unconstrained-signal.circom
Normal file
@@ -0,0 +1,19 @@
|
||||
pragma circom 2.0.0;
|
||||
|
||||
template ToBits(n) {
|
||||
signal input in;
|
||||
signal output out[n];
|
||||
var value = 0;
|
||||
var power = 1;
|
||||
var result = 0;
|
||||
for (var i = 0; i < n; i++) {
|
||||
out[i] <-- (in >> i) & 1;
|
||||
// out[i] * (out[i] - 1) === 0;
|
||||
result += out[i] * power;
|
||||
power = power + power;
|
||||
}
|
||||
// The output is unconstrained.
|
||||
// result === in;
|
||||
}
|
||||
|
||||
component main {public [in]} = ToBits(256);
|
||||
18
examples/unused-variable.circom
Normal file
18
examples/unused-variable.circom
Normal file
@@ -0,0 +1,18 @@
|
||||
pragma circom 2.0.0;
|
||||
|
||||
template ToBits(n) {
|
||||
signal input in;
|
||||
signal output out[n];
|
||||
var value = 0; // The variable value is unused.
|
||||
var power = 1;
|
||||
var result = 0;
|
||||
for (var i = 0; i < n; i++) {
|
||||
out[i] <-- (in >> i) & 1;
|
||||
out[i] * (out[i] - 1) === 0;
|
||||
result += out[i] * power;
|
||||
power = power + power;
|
||||
}
|
||||
result === in;
|
||||
}
|
||||
|
||||
component main {public [in]} = ToBits(256);
|
||||
23
parser/Cargo.toml
Normal file
23
parser/Cargo.toml
Normal file
@@ -0,0 +1,23 @@
|
||||
[package]
|
||||
name = "parser"
|
||||
version = "2.0.1"
|
||||
authors = ["Hermenegildo <hermegar@ucm.es>"]
|
||||
edition = "2018"
|
||||
build = "build.rs"
|
||||
|
||||
[build-dependencies]
|
||||
rustc-hex = "2.0.1"
|
||||
lalrpop = { version = "0.18.1", features = ["lexer"] }
|
||||
num-bigint-dig = "0.6.0"
|
||||
num-traits = "0.2.6"
|
||||
|
||||
[dependencies]
|
||||
program_structure = {path = "../program_structure"}
|
||||
lalrpop = { version = "0.18.1", features = ["lexer"] }
|
||||
lalrpop-util = "0.18.1"
|
||||
regex = "1.1.2"
|
||||
rustc-hex = "2.0.1"
|
||||
num-bigint-dig = "0.6.0"
|
||||
num-traits = "0.2.6"
|
||||
serde = "1.0.82"
|
||||
serde_derive = "1.0.91"
|
||||
5
parser/build.rs
Normal file
5
parser/build.rs
Normal file
@@ -0,0 +1,5 @@
|
||||
extern crate lalrpop;
|
||||
|
||||
fn main() {
|
||||
lalrpop::process_root().unwrap();
|
||||
}
|
||||
87
parser/src/errors.rs
Normal file
87
parser/src/errors.rs
Normal file
@@ -0,0 +1,87 @@
|
||||
use program_structure::error_code::ReportCode;
|
||||
use program_structure::error_definition::Report;
|
||||
use program_structure::file_definition::{FileID, FileLocation};
|
||||
use program_structure::abstract_syntax_tree::ast::Version;
|
||||
|
||||
pub struct UnclosedCommentError {
|
||||
pub location: FileLocation,
|
||||
pub file_id: FileID,
|
||||
}
|
||||
|
||||
impl UnclosedCommentError {
|
||||
pub fn produce_report(error: Self) -> Report {
|
||||
let mut report = Report::error(format!("unterminated /* */"), ReportCode::ParseFail);
|
||||
report.add_primary(error.location, error.file_id, format!("Comment starts here"));
|
||||
report
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ParsingError {
|
||||
pub location: FileLocation,
|
||||
pub file_id: FileID,
|
||||
pub msg: String,
|
||||
}
|
||||
|
||||
impl ParsingError {
|
||||
pub fn produce_report(error: Self) -> Report {
|
||||
let mut report = Report::error(error.msg, ReportCode::ParseFail);
|
||||
report.add_primary(error.location, error.file_id, format!("Invalid syntax"));
|
||||
report
|
||||
}
|
||||
}
|
||||
|
||||
pub struct FileOsError {
|
||||
pub path: String,
|
||||
}
|
||||
impl FileOsError {
|
||||
pub fn produce_report(error: Self) -> Report {
|
||||
Report::error(format!("Could not open file {}", error.path), ReportCode::ParseFail)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct NoMainError;
|
||||
impl NoMainError {
|
||||
pub fn produce_report() -> Report {
|
||||
Report::error(
|
||||
format!("No main specified in the project structure"),
|
||||
ReportCode::NoMainFoundInProject,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct MultipleMainError;
|
||||
impl MultipleMainError {
|
||||
pub fn produce_report() -> Report {
|
||||
Report::error(
|
||||
format!("Multiple main components in the project structure"),
|
||||
ReportCode::MultipleMainInComponent,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct CompilerVersionError{
|
||||
pub path: String,
|
||||
pub required_version: Version,
|
||||
pub version: Version,
|
||||
}
|
||||
impl CompilerVersionError {
|
||||
pub fn produce_report(error: Self) -> Report {
|
||||
Report::error(
|
||||
format!("File {} requires pragma version {:?} that is not supported by the compiler (version {:?})", error.path, error.required_version, error.version ),
|
||||
ReportCode::CompilerVersionError,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct NoCompilerVersionWarning{
|
||||
pub path: String,
|
||||
pub version: Version,
|
||||
}
|
||||
impl NoCompilerVersionWarning {
|
||||
pub fn produce_report(error: Self) -> Report {
|
||||
Report::warning(
|
||||
format!("File {} does not include pragma version. Assuming pragma version {:?}", error.path, error.version),
|
||||
ReportCode::NoCompilerVersionWarning,
|
||||
)
|
||||
}
|
||||
}
|
||||
47
parser/src/include_logic.rs
Normal file
47
parser/src/include_logic.rs
Normal file
@@ -0,0 +1,47 @@
|
||||
use super::errors::FileOsError;
|
||||
use program_structure::error_definition::Report;
|
||||
use std::collections::HashSet;
|
||||
use std::path::PathBuf;
|
||||
|
||||
pub struct FileStack {
|
||||
current_location: PathBuf,
|
||||
black_paths: HashSet<PathBuf>,
|
||||
stack: Vec<PathBuf>,
|
||||
}
|
||||
|
||||
impl FileStack {
|
||||
pub fn new(src: PathBuf) -> FileStack {
|
||||
let mut location = src.clone();
|
||||
location.pop();
|
||||
FileStack { current_location: location, black_paths: HashSet::new(), stack: vec![src] }
|
||||
}
|
||||
|
||||
pub fn add_include(f_stack: &mut FileStack, path: String) -> Result<(), Report> {
|
||||
let mut crr = f_stack.current_location.clone();
|
||||
crr.push(path.clone());
|
||||
let path = std::fs::canonicalize(crr)
|
||||
.map_err(|_| FileOsError { path: path.clone() })
|
||||
.map_err(|e| FileOsError::produce_report(e))?;
|
||||
if !f_stack.black_paths.contains(&path) {
|
||||
f_stack.stack.push(path);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn take_next(f_stack: &mut FileStack) -> Option<PathBuf> {
|
||||
loop {
|
||||
match f_stack.stack.pop() {
|
||||
None => {
|
||||
break None;
|
||||
}
|
||||
Some(file) if !f_stack.black_paths.contains(&file) => {
|
||||
f_stack.current_location = file.clone();
|
||||
f_stack.current_location.pop();
|
||||
f_stack.black_paths.insert(file.clone());
|
||||
break Some(file);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
525
parser/src/lang.lalrpop
Normal file
525
parser/src/lang.lalrpop
Normal file
@@ -0,0 +1,525 @@
|
||||
use num_bigint::BigInt;
|
||||
use program_structure::statement_builders::*;
|
||||
use program_structure::expression_builders::*;
|
||||
use program_structure::ast::*;
|
||||
use program_structure::ast_shortcuts::{self,Symbol};
|
||||
use std::str::FromStr;
|
||||
|
||||
grammar;
|
||||
|
||||
// ====================================================================
|
||||
// Body
|
||||
// ====================================================================
|
||||
|
||||
// A identifier list is a comma separated list of identifiers
|
||||
IdentifierListDef : Vec<String> = {
|
||||
<v:(<IDENTIFIER> ",")*> <e:IDENTIFIER> => {
|
||||
let mut v = v;
|
||||
v.push(e);
|
||||
v
|
||||
}
|
||||
};
|
||||
|
||||
// Pragma is included at the start of the file.
|
||||
// Their structure is the following: pragma circom "version of the compiler"
|
||||
ParsePragma : Version = { // maybe change to usize instead of BigInt
|
||||
"pragma circom" <version: Version> ";"
|
||||
=> version,
|
||||
};
|
||||
|
||||
|
||||
// Includes are added at the start of the file.
|
||||
// Their structure is the following: #include "path to the file"
|
||||
ParseInclude : String = {
|
||||
"include" <file: STRING> ";"
|
||||
=> file,
|
||||
};
|
||||
|
||||
// Parsing a program requires:
|
||||
// Parsing the "pragma" instruction, if there is one
|
||||
// Parsing "includes" instructions, if there is anyone,
|
||||
// Parsing function and template definitions,
|
||||
// Parsing the declaration of the main component
|
||||
pub ParseAst : AST = {
|
||||
<s:@L> <pragma: ParsePragma> <includes: ParseInclude*> <definitions: ParseDefinition*> <main: ParseMainComponent> <e:@R>
|
||||
=> AST::new(Meta::new(s,e), Option::Some(pragma), includes,definitions,Option::Some(main)),
|
||||
|
||||
<s:@L> <includes: ParseInclude*> <definitions: ParseDefinition*> <main: ParseMainComponent> <e:@R>
|
||||
=> AST::new(Meta::new(s,e),Option::None, includes,definitions,Option::Some(main)),
|
||||
|
||||
<s:@L> <pragma: ParsePragma> <includes: ParseInclude*> <definitions: ParseDefinition*> <e:@R>
|
||||
=> AST::new(Meta::new(s,e), Option::Some(pragma), includes,definitions,Option::None),
|
||||
|
||||
<s:@L> <includes: ParseInclude*> <definitions: ParseDefinition*> <e:@R>
|
||||
=> AST::new(Meta::new(s,e), Option::None,includes,definitions,Option::None),
|
||||
};
|
||||
|
||||
// ====================================================================
|
||||
// Definitions
|
||||
// ====================================================================
|
||||
|
||||
// The private list of the main component stands for the
|
||||
// list of private input signals
|
||||
ParsePublicList : Vec<String> = {
|
||||
"{" "public" "[" <id: IdentifierListDef> "]" "}" => id,
|
||||
};
|
||||
|
||||
pub ParseMainComponent : MainComponent = {
|
||||
<s:@L> "component" "main" <public_list: ParsePublicList?> "=" <init: ParseExpression> ";" <e:@L>
|
||||
=> match public_list {
|
||||
None => build_main_component(Vec::new(),init),
|
||||
Some(list) => build_main_component(list,init)
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
pub ParseDefinition : Definition = {
|
||||
<s:@L> "function" <name: IDENTIFIER> "(" <args:@L> <arg_names: IdentifierListDef?> <arge:@R> ")" <body: ParseBlock> <e:@R>
|
||||
=> match arg_names {
|
||||
None
|
||||
=> build_function(Meta::new(s,e),name,Vec::new(),args..arge,body),
|
||||
Some(a)
|
||||
=> build_function(Meta::new(s,e),name,a,args..arge,body),
|
||||
},
|
||||
<s:@L> "template" <parallel: "parallel"?> <name: IDENTIFIER> "(" <args:@L> <arg_names: IdentifierListDef?> <arge:@R> ")" <body: ParseBlock> <e:@R>
|
||||
=> match arg_names {
|
||||
None
|
||||
=> build_template(Meta::new(s,e), name, Vec::new(), args..arge, body, parallel.is_some()),
|
||||
Some(a)
|
||||
=> build_template(Meta::new(s,e), name, a, args..arge, body, parallel.is_some()),
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
// ====================================================================
|
||||
// VariableDefinitions
|
||||
// ====================================================================
|
||||
|
||||
ParseElementType : SignalElementType = {
|
||||
"FieldElement" => SignalElementType::FieldElement,
|
||||
"Binary" => SignalElementType::Binary,
|
||||
};
|
||||
|
||||
ParseSignalType: SignalType = {
|
||||
"input" => SignalType::Input,
|
||||
"output" => SignalType::Output
|
||||
};
|
||||
|
||||
SignalHeader : VariableType = {
|
||||
"signal" <element_type: (":" <ParseElementType>)?> <signal_type: ParseSignalType?>
|
||||
=> {
|
||||
let e = match element_type {
|
||||
None => SignalElementType::FieldElement,
|
||||
Some(t) => t,
|
||||
};
|
||||
let s = match signal_type {
|
||||
None => SignalType::Intermediate,
|
||||
Some(st) => st,
|
||||
};
|
||||
VariableType::Signal(s,e)
|
||||
}
|
||||
};
|
||||
|
||||
// ====================================================================
|
||||
// Statements
|
||||
// ====================================================================
|
||||
|
||||
// A Initialization is either just the name of a variable or
|
||||
// the name followed by a expression that initialices the variable.
|
||||
|
||||
SimpleSymbol : Symbol = {
|
||||
<name:IDENTIFIER> <dims:ParseArrayAcc*>
|
||||
=> Symbol {
|
||||
name,
|
||||
is_array: dims,
|
||||
init: Option::None,
|
||||
},
|
||||
}
|
||||
|
||||
ComplexSymbol : Symbol = {
|
||||
<name:IDENTIFIER> <dims:ParseArrayAcc*> "=" <rhe: ParseExpression>
|
||||
=> Symbol {
|
||||
name,
|
||||
is_array: dims,
|
||||
init: Option::Some(rhe),
|
||||
},
|
||||
};
|
||||
|
||||
SomeSymbol : Symbol = {
|
||||
ComplexSymbol,
|
||||
SimpleSymbol,
|
||||
}
|
||||
|
||||
// A declaration is the definition of a type followed by the initialization
|
||||
ParseDeclaration : Statement = {
|
||||
|
||||
<s:@L> "var" <symbols:(<SomeSymbol> ",")*> <symbol: SomeSymbol> <e:@R> => {
|
||||
let mut symbols = symbols;
|
||||
let meta = Meta::new(s,e);
|
||||
let xtype = VariableType::Var;
|
||||
symbols.push(symbol);
|
||||
ast_shortcuts::split_declaration_into_single_nodes(meta,xtype,symbols)
|
||||
},
|
||||
<s:@L> "component" <symbols:(<SomeSymbol> ",")*> <symbol: SomeSymbol> <e:@R> => {
|
||||
let mut symbols = symbols;
|
||||
let meta = Meta::new(s,e);
|
||||
let xtype = VariableType::Component;
|
||||
symbols.push(symbol);
|
||||
ast_shortcuts::split_declaration_into_single_nodes(meta,xtype,symbols)
|
||||
},
|
||||
<s:@L><xtype: SignalHeader> <symbols:(<SimpleSymbol> ",")*> <symbol: SimpleSymbol> <e:@R>
|
||||
=> {
|
||||
let mut symbols = symbols;
|
||||
let meta = Meta::new(s,e);
|
||||
symbols.push(symbol);
|
||||
ast_shortcuts::split_declaration_into_single_nodes(meta,xtype,symbols)
|
||||
},
|
||||
};
|
||||
ParseSubstitution : Statement = {
|
||||
<s:@L> <variable: ParseVariable> <op: ParseAssignOp> <rhe: ParseExpression> <e:@R>
|
||||
=> {let (name,access) = variable;
|
||||
build_substitution(Meta::new(s,e),name,access,op,rhe)
|
||||
},
|
||||
<s:@L> <lhe: ParseExpression> "-->" <variable: ParseVariable> <e:@R>
|
||||
=> {let (name,access) = variable;
|
||||
build_substitution(Meta::new(s,e),name,access,AssignOp::AssignSignal,lhe)
|
||||
},
|
||||
<s:@L> <lhe: ParseExpression> "==>" <variable: ParseVariable> <e:@R>
|
||||
=> {let (name,access) = variable;
|
||||
build_substitution(Meta::new(s,e),name,access,AssignOp::AssignConstraintSignal,lhe)
|
||||
},
|
||||
<s:@L> <variable: ParseVariable> "\\=" <rhe: ParseExpression> <e:@R>
|
||||
=> ast_shortcuts::assign_with_op_shortcut(ExpressionInfixOpcode::IntDiv,Meta::new(s,e),variable,rhe),
|
||||
|
||||
<s:@L> <variable: ParseVariable> "**=" <rhe: ParseExpression> <e:@R>
|
||||
=> ast_shortcuts::assign_with_op_shortcut(ExpressionInfixOpcode::Pow,Meta::new(s,e),variable,rhe),
|
||||
|
||||
<s:@L> <variable: ParseVariable> "+=" <rhe: ParseExpression> <e:@R>
|
||||
=> ast_shortcuts::assign_with_op_shortcut(ExpressionInfixOpcode::Add,Meta::new(s,e),variable,rhe),
|
||||
|
||||
<s:@L> <variable: ParseVariable> "-=" <rhe: ParseExpression> <e:@R>
|
||||
=> ast_shortcuts::assign_with_op_shortcut(ExpressionInfixOpcode::Sub,Meta::new(s,e),variable,rhe),
|
||||
|
||||
<s:@L> <variable: ParseVariable> "*=" <rhe: ParseExpression> <e:@R>
|
||||
=> ast_shortcuts::assign_with_op_shortcut(ExpressionInfixOpcode::Mul,Meta::new(s,e),variable,rhe),
|
||||
|
||||
<s:@L> <variable: ParseVariable> "/=" <rhe: ParseExpression> <e:@R>
|
||||
=> ast_shortcuts::assign_with_op_shortcut(ExpressionInfixOpcode::Div,Meta::new(s,e),variable,rhe),
|
||||
|
||||
<s:@L> <variable: ParseVariable> "%=" <rhe: ParseExpression> <e:@R>
|
||||
=> ast_shortcuts::assign_with_op_shortcut(ExpressionInfixOpcode::Mod,Meta::new(s,e),variable,rhe),
|
||||
|
||||
<s:@L> <variable: ParseVariable> "<<=" <rhe: ParseExpression> <e:@R>
|
||||
=> ast_shortcuts::assign_with_op_shortcut(ExpressionInfixOpcode::ShiftL,Meta::new(s,e),variable,rhe),
|
||||
|
||||
<s:@L> <variable: ParseVariable> ">>=" <rhe: ParseExpression> <e:@R>
|
||||
=> ast_shortcuts::assign_with_op_shortcut(ExpressionInfixOpcode::ShiftR,Meta::new(s,e),variable,rhe),
|
||||
|
||||
<s:@L> <variable: ParseVariable> "&=" <rhe: ParseExpression> <e:@R>
|
||||
=> ast_shortcuts::assign_with_op_shortcut(ExpressionInfixOpcode::BitAnd,Meta::new(s,e),variable,rhe),
|
||||
|
||||
<s:@L> <variable: ParseVariable> "|=" <rhe: ParseExpression> <e:@R>
|
||||
=> ast_shortcuts::assign_with_op_shortcut(ExpressionInfixOpcode::BitOr,Meta::new(s,e),variable,rhe),
|
||||
|
||||
<s:@L> <variable: ParseVariable> "^=" <rhe: ParseExpression> <e:@R>
|
||||
=> ast_shortcuts::assign_with_op_shortcut(ExpressionInfixOpcode::BitXor,Meta::new(s,e),variable,rhe),
|
||||
|
||||
<s:@L> <variable: ParseVariable> "++" <e:@R>
|
||||
=> ast_shortcuts::plusplus(Meta::new(s,e),variable),
|
||||
|
||||
<s:@L> <variable: ParseVariable> "--" <e:@R>
|
||||
=> ast_shortcuts::subsub(Meta::new(s,e),variable),
|
||||
};
|
||||
|
||||
ParseBlock : Statement = {
|
||||
<s:@L> "{" <stmts :ParseStatement3*> "}" <e:@R>
|
||||
=> build_block(Meta::new(s,e),stmts),
|
||||
};
|
||||
|
||||
pub ParseStatement : Statement = {
|
||||
ParseStatement0
|
||||
};
|
||||
|
||||
ParseElse<StmtLevel> : Statement = {
|
||||
"else" <else_case: StmtLevel> => else_case,
|
||||
};
|
||||
|
||||
ParseStatement0 : Statement = {
|
||||
ParseStmt0NB,
|
||||
ParseStatement1
|
||||
};
|
||||
|
||||
ParseStmt0NB : Statement = {
|
||||
<s:@L> "if" "(" <cond: ParseExpression> ")" <if_case: ParseStmt0NB> <e:@R>
|
||||
=> build_conditional_block(Meta::new(s,e),cond,if_case,Option::None),
|
||||
|
||||
<s:@L> "if" "(" <cond: ParseExpression> ")" <if_case: ParseStatement1> <e:@R>
|
||||
=> build_conditional_block(Meta::new(s,e),cond,if_case,Option::None),
|
||||
|
||||
<s:@L> "if" "(" <cond: ParseExpression> ")" <if_case: ParseStatement1> <else_case: ParseElse<ParseStmt0NB>><e:@R>
|
||||
=> build_conditional_block(Meta::new(s,e),cond,if_case,Option::Some(else_case)),
|
||||
};
|
||||
|
||||
ParseStatement1 : Statement = {
|
||||
<s:@L> "if" "(" <cond: ParseExpression> ")" <if_case: ParseStatement1> <else_case: ParseElse<ParseStatement1>><e:@R>
|
||||
=> build_conditional_block(Meta::new(s,e),cond,if_case,Option::Some(else_case)),
|
||||
ParseStatement2
|
||||
};
|
||||
ParseStatement2 : Statement = {
|
||||
<s:@L> "for" "(" <init: ParseDeclaration> ";" <cond: ParseExpression> ";" <step: ParseSubstitution> ")" <body: ParseStatement2> <e:@R>
|
||||
=> ast_shortcuts::for_into_while(Meta::new(s,e),init,cond,step,body),
|
||||
|
||||
<s:@L> "for" "(" <init: ParseSubstitution> ";" <cond: ParseExpression> ";" <step: ParseSubstitution> ")" <body: ParseStatement2> <e:@R>
|
||||
=> ast_shortcuts::for_into_while(Meta::new(s,e),init,cond,step,body),
|
||||
|
||||
<s:@L>"while" "(" <cond: ParseExpression> ")" <stmt: ParseStatement2> <e:@R>
|
||||
=> build_while_block(Meta::new(s,e),cond,stmt),
|
||||
|
||||
<s:@L> "return" <value: ParseExpression> ";"<e:@R>
|
||||
=> build_return(Meta::new(s,e),value),
|
||||
|
||||
<subs: ParseSubstitution> ";"
|
||||
=> subs,
|
||||
|
||||
<s:@L> <lhe: ParseExpression> "===" <rhe: ParseExpression> ";" <e:@R>
|
||||
=> build_constraint_equality(Meta::new(s,e),lhe,rhe),
|
||||
|
||||
<s:@L> "log" "(" <arg: ParseExpression> ")" ";" <e:@R>
|
||||
=> build_log_call(Meta::new(s,e),arg),
|
||||
|
||||
<s:@L> "assert" "(" <arg: ParseExpression> ")" ";" <e:@R>
|
||||
=> build_assert(Meta::new(s,e),arg),
|
||||
|
||||
ParseBlock
|
||||
};
|
||||
|
||||
ParseStatement3 : Statement = {
|
||||
<dec: ParseDeclaration> ";"
|
||||
=> dec,
|
||||
|
||||
ParseStatement
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
// ====================================================================
|
||||
// Variable
|
||||
// ====================================================================
|
||||
|
||||
ParseVarAccess : Access = {
|
||||
<arr_dec: ParseArrayAcc> => build_array_access(arr_dec),
|
||||
<component_acc: ParseComponentAcc> => build_component_access(component_acc),
|
||||
};
|
||||
ParseArrayAcc: Expression = {
|
||||
"["<dim: ParseExpression>"]" => dim
|
||||
};
|
||||
ParseComponentAcc: String = {
|
||||
"." <id: IDENTIFIER> => id,
|
||||
};
|
||||
ParseVariable : (String,Vec<Access>) = {
|
||||
<name:IDENTIFIER> <access: ParseVarAccess*>
|
||||
=> (name,access),
|
||||
};
|
||||
// ====================================================================
|
||||
// Expression
|
||||
// ====================================================================
|
||||
|
||||
Listable: Vec<Expression> = {
|
||||
<e:(<ParseExpression> ",")*> <tail: ParseExpression>
|
||||
=> {
|
||||
let mut e = e;
|
||||
e.push(tail);
|
||||
e
|
||||
},
|
||||
};
|
||||
|
||||
InfixOpTier<Op,NextTier> : Expression = {
|
||||
<s:@L> <lhe:InfixOpTier<Op,NextTier>> <infix_op:Op> <rhe:NextTier> <e: @R>
|
||||
=> build_infix(Meta::new(s,e),lhe,infix_op,rhe),
|
||||
|
||||
NextTier
|
||||
};
|
||||
|
||||
PrefixOpTier<Op,NextTier >: Expression = {
|
||||
<s:@L> <prefix_op:Op> <rhe:NextTier> <e:@R>
|
||||
=> build_prefix(Meta::new(s,e),prefix_op,rhe),
|
||||
|
||||
NextTier
|
||||
};
|
||||
|
||||
|
||||
pub ParseExpression: Expression = {
|
||||
Expression13,
|
||||
Expression12,
|
||||
};
|
||||
|
||||
// ops: e ? a : i
|
||||
Expression13 : Expression = {
|
||||
<s:@L> <cond: Expression12> "?" <if_true: Expression12> ":" <if_false: Expression12> <e:@R>
|
||||
=> build_inline_switch_op(Meta::new(s,e),cond,if_true,if_false),
|
||||
};
|
||||
|
||||
// ops: ||
|
||||
Expression12 = InfixOpTier<ParseBoolOr,Expression11>;
|
||||
|
||||
// ops: &&
|
||||
Expression11 = InfixOpTier<ParseBoolAnd,Expression10>;
|
||||
|
||||
// ops: == != < > <= >=
|
||||
Expression10 = InfixOpTier<ParseCmpOpCodes,Expression9>;
|
||||
|
||||
// ops: |
|
||||
Expression9 = InfixOpTier<ParseBitOr,Expression8>;
|
||||
|
||||
// ops: ^
|
||||
Expression8 = InfixOpTier<ParseBitXOR,Expression7>;
|
||||
|
||||
// ops: &
|
||||
Expression7 = InfixOpTier<ParseBitAnd,Expression6>;
|
||||
|
||||
// ops: << >>
|
||||
Expression6 = InfixOpTier<ParseShift,Expression5>;
|
||||
|
||||
// ops: + -
|
||||
Expression5 = InfixOpTier<ParseAddAndSub,Expression4>;
|
||||
|
||||
// ops: * / \\ %
|
||||
Expression4 = InfixOpTier<ParseMulDiv,Expression3>;
|
||||
|
||||
// ops: **
|
||||
Expression3 = InfixOpTier<ParseExp,Expression2>;
|
||||
|
||||
// ops: Unary - ! ~
|
||||
Expression2 = PrefixOpTier<ParseExpressionPrefixOpcode,Expression1>;
|
||||
|
||||
// function call, array inline
|
||||
Expression1: Expression = {
|
||||
<s:@L> <id: IDENTIFIER> "(" <args: Listable?> ")" <e:@R>
|
||||
=> match args {
|
||||
None => build_call(Meta::new(s,e),id,Vec::new()),
|
||||
Some(a) => build_call(Meta::new(s,e),id,a),
|
||||
},
|
||||
|
||||
<s:@L> "[" <values: Listable> "]" <e:@R>
|
||||
=> build_array_in_line(Meta::new(s,e),values),
|
||||
|
||||
Expression0,
|
||||
};
|
||||
|
||||
// Literal, parentheses
|
||||
Expression0: Expression = {
|
||||
<s:@L> <variable: ParseVariable> <e:@L>
|
||||
=> {
|
||||
let (name,access) = variable;
|
||||
build_variable(Meta::new(s,e),name,access)
|
||||
},
|
||||
|
||||
<s:@L> <value:DECNUMBER> <e:@L>
|
||||
=> build_number(Meta::new(s,e),value),
|
||||
|
||||
<s:@L> <value:HEXNUMBER> <e:@L>
|
||||
=> build_number(Meta::new(s,e),value),
|
||||
|
||||
"(" <Expression12> ")"
|
||||
};
|
||||
|
||||
|
||||
// ====================================================================
|
||||
// Terminals
|
||||
// ====================================================================
|
||||
|
||||
|
||||
|
||||
ParseExpressionPrefixOpcode: ExpressionPrefixOpcode = {
|
||||
"!" => ExpressionPrefixOpcode::BoolNot,
|
||||
"~" => ExpressionPrefixOpcode::Complement,
|
||||
"-" => ExpressionPrefixOpcode::Sub,
|
||||
};
|
||||
|
||||
ParseBoolOr : ExpressionInfixOpcode = {
|
||||
"||" => ExpressionInfixOpcode::BoolOr,
|
||||
};
|
||||
|
||||
ParseBoolAnd : ExpressionInfixOpcode = {
|
||||
"&&" => ExpressionInfixOpcode::BoolAnd,
|
||||
};
|
||||
|
||||
ParseCmpOpCodes : ExpressionInfixOpcode = {
|
||||
"==" => ExpressionInfixOpcode::Eq,
|
||||
"!=" => ExpressionInfixOpcode::NotEq,
|
||||
"<" => ExpressionInfixOpcode::Lesser,
|
||||
">" => ExpressionInfixOpcode::Greater,
|
||||
"<=" => ExpressionInfixOpcode::LesserEq,
|
||||
">=" => ExpressionInfixOpcode::GreaterEq,
|
||||
};
|
||||
|
||||
ParseBitOr : ExpressionInfixOpcode = {
|
||||
"|" => ExpressionInfixOpcode::BitOr,
|
||||
};
|
||||
|
||||
ParseBitAnd : ExpressionInfixOpcode = {
|
||||
"&" => ExpressionInfixOpcode::BitAnd,
|
||||
};
|
||||
|
||||
ParseShift : ExpressionInfixOpcode = {
|
||||
"<<" => ExpressionInfixOpcode::ShiftL,
|
||||
">>" => ExpressionInfixOpcode::ShiftR,
|
||||
};
|
||||
|
||||
ParseAddAndSub : ExpressionInfixOpcode = {
|
||||
"+" => ExpressionInfixOpcode::Add,
|
||||
"-" => ExpressionInfixOpcode::Sub,
|
||||
};
|
||||
|
||||
ParseMulDiv : ExpressionInfixOpcode = {
|
||||
"*" => ExpressionInfixOpcode::Mul,
|
||||
"/" => ExpressionInfixOpcode::Div,
|
||||
"\\" => ExpressionInfixOpcode::IntDiv,
|
||||
"%" => ExpressionInfixOpcode::Mod,
|
||||
};
|
||||
|
||||
ParseExp : ExpressionInfixOpcode = {
|
||||
"**" => ExpressionInfixOpcode::Pow,
|
||||
};
|
||||
|
||||
ParseBitXOR : ExpressionInfixOpcode = {
|
||||
"^" => ExpressionInfixOpcode::BitXor,
|
||||
};
|
||||
|
||||
|
||||
ParseAssignOp: AssignOp = {
|
||||
"=" => AssignOp::AssignVar,
|
||||
"<--" => AssignOp::AssignSignal,
|
||||
"<==" => AssignOp::AssignConstraintSignal,
|
||||
};
|
||||
|
||||
DECNUMBER: BigInt = {
|
||||
r"[0-9]+" => BigInt::parse_bytes(&<>.as_bytes(),10).expect("failed to parse base10")
|
||||
};
|
||||
|
||||
HEXNUMBER : BigInt = {
|
||||
r"0x[0-9A-Fa-f]*" => BigInt::parse_bytes(&(<>.as_bytes()[2..]),16).expect("failed to parse base16")
|
||||
};
|
||||
|
||||
IDENTIFIER : String = {
|
||||
r"[$_]*[a-zA-Z][a-zA-Z$_0-9]*" => String::from(<>)
|
||||
};
|
||||
|
||||
STRING : String = {
|
||||
<s:r#""[^"]*""#> => String::from(&s[1..s.len()-1])
|
||||
};
|
||||
|
||||
SMALL_DECNUMBER: usize = {
|
||||
r"[0-9]+" => usize::from_str(<>).expect("failed to parse number")
|
||||
};
|
||||
|
||||
|
||||
// Version used by pragma to describe the compiler, its syntax is Number1.Number2.Number3...
|
||||
Version : Version = {
|
||||
<version: SMALL_DECNUMBER> "." <subversion:SMALL_DECNUMBER> "." <subsubversion:SMALL_DECNUMBER> => {
|
||||
(version, subversion, subsubversion)
|
||||
}
|
||||
};
|
||||
105
parser/src/lib.rs
Normal file
105
parser/src/lib.rs
Normal file
@@ -0,0 +1,105 @@
|
||||
extern crate num_bigint_dig as num_bigint;
|
||||
extern crate num_traits;
|
||||
extern crate serde;
|
||||
extern crate serde_derive;
|
||||
#[macro_use]
|
||||
extern crate lalrpop_util;
|
||||
|
||||
lalrpop_mod!(pub lang);
|
||||
|
||||
|
||||
mod errors;
|
||||
mod include_logic;
|
||||
mod parser_logic;
|
||||
use include_logic::FileStack;
|
||||
use program_structure::error_definition::{Report, ReportCollection};
|
||||
use program_structure::file_definition::{FileLibrary};
|
||||
use program_structure::program_archive::ProgramArchive;
|
||||
use std::path::PathBuf;
|
||||
use std::str::FromStr;
|
||||
|
||||
pub type Version = (usize, usize, usize);
|
||||
|
||||
|
||||
pub fn run_parser(file: String, version: &str) -> Result<(ProgramArchive, ReportCollection), (FileLibrary, ReportCollection)> {
|
||||
let mut file_library = FileLibrary::new();
|
||||
let mut definitions = Vec::new();
|
||||
let mut main_components = Vec::new();
|
||||
let mut file_stack = FileStack::new(PathBuf::from(file));
|
||||
let mut warnings = Vec::new();
|
||||
|
||||
while let Some(crr_file) = FileStack::take_next(&mut file_stack) {
|
||||
let (path, src) = open_file(crr_file).map_err(|e| (file_library.clone(), vec![e]))?;
|
||||
let file_id = file_library.add_file(path.clone(), src.clone());
|
||||
let program =
|
||||
parser_logic::parse_file(&src, file_id).map_err(|e| (file_library.clone(), vec![e]))?;
|
||||
|
||||
if let Some(main) = program.main_component {
|
||||
main_components.push((file_id, main));
|
||||
}
|
||||
let includes = program.includes;
|
||||
definitions.push((file_id, program.definitions));
|
||||
for include in includes {
|
||||
FileStack::add_include(&mut file_stack, include)
|
||||
.map_err(|e| (file_library.clone(), vec![e]))?;
|
||||
}
|
||||
warnings.append(&mut check_number_version(path, program.compiler_version, parse_number_version(version)).map_err(|e| (file_library.clone(), vec![e]))?);
|
||||
}
|
||||
|
||||
if main_components.len() == 0 {
|
||||
let report = errors::NoMainError::produce_report();
|
||||
Err((file_library, vec![report]))
|
||||
} else if main_components.len() > 1 {
|
||||
let report = errors::MultipleMainError::produce_report();
|
||||
Err((file_library, vec![report]))
|
||||
}
|
||||
else{
|
||||
let (main_id, main_component) = main_components.pop().unwrap();
|
||||
let result_program_archive =
|
||||
ProgramArchive::new(file_library, main_id, main_component, definitions);
|
||||
match result_program_archive {
|
||||
Err((lib, rep)) => {
|
||||
Err((lib, rep))
|
||||
}
|
||||
Ok(program_archive) => {
|
||||
Ok((program_archive, warnings))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn open_file(path: PathBuf) -> Result<(String, String), Report> /* path, src*/ {
|
||||
use errors::FileOsError;
|
||||
use std::fs::read_to_string;
|
||||
let path_str = format!("{:?}", path);
|
||||
read_to_string(path)
|
||||
.map(|contents| (path_str.clone(), contents))
|
||||
.map_err(|_| FileOsError { path: path_str.clone() })
|
||||
.map_err(|e| FileOsError::produce_report(e))
|
||||
}
|
||||
|
||||
fn parse_number_version(version: &str) -> Version{
|
||||
let version_splitted: Vec<&str> = version.split(".").collect();
|
||||
|
||||
(usize::from_str(version_splitted[0]).unwrap(), usize::from_str(version_splitted[1]).unwrap(), usize::from_str(version_splitted[2]).unwrap())
|
||||
}
|
||||
|
||||
fn check_number_version(file_path: String, version_file: Option<Version>, version_compiler: Version) -> Result<ReportCollection, Report>{
|
||||
use errors::{CompilerVersionError, NoCompilerVersionWarning};
|
||||
if let Some(required_version) = version_file {
|
||||
|
||||
if required_version.0 == version_compiler.0
|
||||
&& required_version.1 == version_compiler.1
|
||||
&& required_version.2 <= version_compiler.2{
|
||||
Ok(vec![])
|
||||
}
|
||||
else{
|
||||
let report = CompilerVersionError::produce_report(CompilerVersionError{path: file_path, required_version: required_version, version: version_compiler});
|
||||
Err(report)
|
||||
}
|
||||
}
|
||||
else{
|
||||
let report = NoCompilerVersionWarning::produce_report(NoCompilerVersionWarning{path: file_path, version: version_compiler});
|
||||
Ok(vec![report])
|
||||
}
|
||||
}
|
||||
100
parser/src/parser_logic.rs
Normal file
100
parser/src/parser_logic.rs
Normal file
@@ -0,0 +1,100 @@
|
||||
use super::errors::{ParsingError, UnclosedCommentError};
|
||||
use super::lang;
|
||||
use program_structure::ast::AST;
|
||||
use program_structure::error_definition::Report;
|
||||
use program_structure::file_definition::FileID;
|
||||
|
||||
pub fn preprocess(expr: &str, file_id: FileID) -> Result<String, Report> {
|
||||
let mut pp = String::new();
|
||||
let mut state = 0;
|
||||
let mut loc = 0;
|
||||
let mut block_start = 0;
|
||||
|
||||
let mut it = expr.chars();
|
||||
while let Some(c0) = it.next() {
|
||||
loc += 1;
|
||||
match (state, c0) {
|
||||
(0, '/') => {
|
||||
loc += 1;
|
||||
match it.next() {
|
||||
Some('/') => {
|
||||
state = 1;
|
||||
pp.push(' ');
|
||||
pp.push(' ');
|
||||
}
|
||||
Some('*') => {
|
||||
block_start = loc;
|
||||
state = 2;
|
||||
pp.push(' ');
|
||||
pp.push(' ');
|
||||
}
|
||||
Some(c1) => {
|
||||
pp.push(c0);
|
||||
pp.push(c1);
|
||||
}
|
||||
None => {
|
||||
pp.push(c0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
(0, _) => pp.push(c0),
|
||||
(1, '\n') => {
|
||||
pp.push(c0);
|
||||
state = 0;
|
||||
}
|
||||
(2, '*') => {
|
||||
loc += 1;
|
||||
match it.next() {
|
||||
Some('/') => {
|
||||
pp.push(' ');
|
||||
pp.push(' ');
|
||||
state = 0;
|
||||
}
|
||||
Some(c) => {
|
||||
pp.push(' ');
|
||||
for _i in 0..c.len_utf8() {
|
||||
pp.push(' ');
|
||||
}
|
||||
}
|
||||
None => {
|
||||
let error =
|
||||
UnclosedCommentError { location: block_start..block_start, file_id };
|
||||
return Err(UnclosedCommentError::produce_report(error));
|
||||
}
|
||||
}
|
||||
}
|
||||
(_, c) => {
|
||||
for _i in 0..c.len_utf8() {
|
||||
pp.push(' ');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(pp)
|
||||
}
|
||||
|
||||
pub fn parse_file(src: &str, file_id: FileID) -> Result<AST, Report> {
|
||||
use lalrpop_util::ParseError::*;
|
||||
lang::ParseAstParser::new()
|
||||
.parse(&preprocess(src, file_id)?)
|
||||
.map_err(|parse_error| match parse_error {
|
||||
InvalidToken { location } => ParsingError {
|
||||
file_id,
|
||||
msg: format!("{:?}", parse_error),
|
||||
location: location..location,
|
||||
},
|
||||
UnrecognizedToken { ref token, .. } => ParsingError {
|
||||
file_id,
|
||||
msg: format!("{:?}", parse_error),
|
||||
location: token.0..token.2,
|
||||
},
|
||||
ExtraToken { ref token } => ParsingError {
|
||||
file_id,
|
||||
msg: format!("{:?}", parse_error),
|
||||
location: token.0..token.2,
|
||||
},
|
||||
_ => ParsingError { file_id, msg: format!("{:?}", parse_error), location: 0..0 },
|
||||
})
|
||||
.map_err(|parsing_error| ParsingError::produce_report(parsing_error))
|
||||
}
|
||||
10
program_analysis/Cargo.toml
Normal file
10
program_analysis/Cargo.toml
Normal file
@@ -0,0 +1,10 @@
|
||||
[package]
|
||||
name = "program_analysis"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1.0"
|
||||
log = "0.4"
|
||||
num-bigint-dig = "0.6"
|
||||
program_structure = { path = "../program_structure" }
|
||||
34
program_analysis/src/errors.rs
Normal file
34
program_analysis/src/errors.rs
Normal file
@@ -0,0 +1,34 @@
|
||||
use program_structure::error_code::ReportCode;
|
||||
use program_structure::error_definition::Report;
|
||||
use program_structure::file_definition::{FileID, FileLocation};
|
||||
|
||||
pub struct ShadowedVariableWarning {
|
||||
pub name: String,
|
||||
pub primary_file_id: FileID,
|
||||
pub primary_location: FileLocation,
|
||||
pub secondary_file_id: FileID,
|
||||
pub secondary_location: FileLocation,
|
||||
}
|
||||
|
||||
impl ShadowedVariableWarning {
|
||||
pub fn produce_report(error: Self) -> Report {
|
||||
let mut report = Report::warning(
|
||||
format!("Declaration of variable '{}' shadows previous declaration", error.name),
|
||||
ReportCode::ShadowedVariable,
|
||||
);
|
||||
report.add_primary(
|
||||
error.primary_location,
|
||||
error.primary_file_id,
|
||||
"shadowing declaration here".to_string()
|
||||
);
|
||||
report.add_secondary(
|
||||
error.secondary_location,
|
||||
error.secondary_file_id,
|
||||
Some("shadowed variable is declared here".to_string())
|
||||
);
|
||||
report.add_note(
|
||||
format!("Consider renaming the second occurence of '{}'", error.name)
|
||||
);
|
||||
report
|
||||
}
|
||||
}
|
||||
4
program_analysis/src/lib.rs
Normal file
4
program_analysis/src/lib.rs
Normal file
@@ -0,0 +1,4 @@
|
||||
pub mod utils;
|
||||
pub mod errors;
|
||||
pub mod visitor;
|
||||
pub mod shadowing_analysis;
|
||||
88
program_analysis/src/shadowing_analysis.rs
Normal file
88
program_analysis/src/shadowing_analysis.rs
Normal file
@@ -0,0 +1,88 @@
|
||||
use log::debug;
|
||||
use anyhow::{anyhow, Result};
|
||||
use std::collections::HashMap;
|
||||
|
||||
use super::visitor::Visitor;
|
||||
use super::errors::ShadowedVariableWarning;
|
||||
use program_structure::program_archive::ProgramArchive;
|
||||
use program_structure::error_definition::ReportCollection;
|
||||
use program_structure::ast::{Expression, Meta, VariableType, Statement};
|
||||
|
||||
#[derive(Default, Clone)]
|
||||
struct Scope {
|
||||
parent: Option<Box<Scope>>,
|
||||
variables: HashMap<String, Meta>,
|
||||
}
|
||||
|
||||
impl Scope {
|
||||
fn push(&self) -> Scope {
|
||||
Scope {
|
||||
parent: Some(Box::new(self.clone())),
|
||||
variables: self.variables.clone()
|
||||
}
|
||||
}
|
||||
|
||||
fn pop(&self) -> Result<Scope> {
|
||||
if let Some(parent) = &self.parent {
|
||||
Ok(*parent.clone())
|
||||
} else {
|
||||
Err(anyhow!("cannot pop outermost scope"))
|
||||
}
|
||||
}
|
||||
|
||||
fn add(&mut self, var: &str, meta: &Meta) -> Option<&Meta> {
|
||||
if self.variables.contains_key(var) {
|
||||
self.variables.get(var)
|
||||
} else {
|
||||
self.variables.insert(var.to_string(), meta.clone());
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct ShadowingAnalysis {
|
||||
scope: Scope,
|
||||
reports: ReportCollection
|
||||
}
|
||||
|
||||
impl ShadowingAnalysis {
|
||||
pub fn new() -> ShadowingAnalysis {
|
||||
ShadowingAnalysis::default()
|
||||
}
|
||||
|
||||
pub fn run(&mut self, program: &ProgramArchive) -> ReportCollection {
|
||||
self.visit_templates(&program.templates);
|
||||
self.visit_functions(&program.functions);
|
||||
self.reports.clone()
|
||||
}
|
||||
}
|
||||
|
||||
impl Visitor for ShadowingAnalysis {
|
||||
fn visit_block(&mut self, _meta: &Meta, stmts: &[Statement]) {
|
||||
self.scope = self.scope.push();
|
||||
for stmt in stmts {
|
||||
self.visit_stmt(stmt);
|
||||
}
|
||||
self.scope
|
||||
.pop()
|
||||
.expect("not in outermost scope");
|
||||
}
|
||||
|
||||
fn visit_expr(&mut self, _: &Expression) {
|
||||
// Override visit_expr to ignore expressions.
|
||||
}
|
||||
|
||||
fn visit_declaration(&mut self, primary_meta: &Meta, _xtype: &VariableType, name: &str, _dimensions: &[Expression], _is_constant: &bool) {
|
||||
if let Some(secondary_meta) = self.scope.add(name, primary_meta) {
|
||||
debug!("declaration of {name} shadows previous declaration");
|
||||
self.reports.push(ShadowedVariableWarning::produce_report(ShadowedVariableWarning {
|
||||
name: name.to_string(),
|
||||
primary_file_id: primary_meta.get_file_id(),
|
||||
primary_location: primary_meta.file_location(),
|
||||
secondary_file_id: secondary_meta.get_file_id(),
|
||||
secondary_location: secondary_meta.file_location(),
|
||||
}));
|
||||
}
|
||||
}
|
||||
}
|
||||
86
program_analysis/src/utils.rs
Normal file
86
program_analysis/src/utils.rs
Normal file
@@ -0,0 +1,86 @@
|
||||
use program_structure::ast::{AssignOp, Expression, ExpressionInfixOpcode, ExpressionPrefixOpcode, Statement};
|
||||
|
||||
pub trait ToString {
|
||||
fn to_string(&self) -> String;
|
||||
}
|
||||
|
||||
impl ToString for Statement {
|
||||
fn to_string(&self) -> String {
|
||||
match self {
|
||||
Statement::IfThenElse { .. } => "if-then-else statement".to_string(),
|
||||
Statement::While { .. } => "while statement".to_string(),
|
||||
Statement::Return { .. } => "return statement".to_string(),
|
||||
Statement::InitializationBlock { .. } => "initialization block".to_string(),
|
||||
Statement::Declaration { name, .. } => format!("declaration of '{name}'"),
|
||||
Statement::Substitution { var, op, .. } => format!("{} '{var}'", &op.to_string()),
|
||||
Statement::ConstraintEquality { .. } => "constraint equality".to_string(),
|
||||
Statement::LogCall { .. } => "log call".to_string(),
|
||||
Statement::Block { .. } => "basic block".to_string(),
|
||||
Statement::Assert { .. } => "assert statement".to_string(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ToString for Expression {
|
||||
fn to_string(&self) -> String {
|
||||
match self {
|
||||
Expression::InfixOp { infix_op, .. } => format!("{} expression", infix_op.to_string()),
|
||||
Expression::PrefixOp { prefix_op, .. } => format!("{} expression", prefix_op.to_string()),
|
||||
Expression::InlineSwitchOp { .. } => "inline switch expression".to_string(),
|
||||
Expression::Variable { name, .. } => format!("variable expression '{name}'"),
|
||||
Expression::Number ( _, value ) => format!("constant expression '{value}'"),
|
||||
Expression::Call { id, .. } => format!("call to '{id}'"),
|
||||
Expression::ArrayInLine { .. } => "inline array expression ".to_string(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ToString for ExpressionInfixOpcode {
|
||||
fn to_string(&self) -> String {
|
||||
match self {
|
||||
ExpressionInfixOpcode::Mul => "mutliplication",
|
||||
ExpressionInfixOpcode::Div => "division",
|
||||
ExpressionInfixOpcode::Add => "addition",
|
||||
ExpressionInfixOpcode::Sub => "subtraction",
|
||||
ExpressionInfixOpcode::Pow => "power",
|
||||
ExpressionInfixOpcode::IntDiv => "integer division",
|
||||
ExpressionInfixOpcode::Mod => "modulo",
|
||||
ExpressionInfixOpcode::ShiftL => "left-shift",
|
||||
ExpressionInfixOpcode::ShiftR => "right-shift",
|
||||
ExpressionInfixOpcode::LesserEq => "less than or equal",
|
||||
ExpressionInfixOpcode::GreaterEq => "greater than or equal",
|
||||
ExpressionInfixOpcode::Lesser => "less than",
|
||||
ExpressionInfixOpcode::Greater => "greater than",
|
||||
ExpressionInfixOpcode::Eq => "equal",
|
||||
ExpressionInfixOpcode::NotEq => "not equal",
|
||||
ExpressionInfixOpcode::BoolOr => "boolean OR",
|
||||
ExpressionInfixOpcode::BoolAnd => "boolean AND",
|
||||
ExpressionInfixOpcode::BitOr => "bitwise OR",
|
||||
ExpressionInfixOpcode::BitAnd => "bitwise AND",
|
||||
ExpressionInfixOpcode::BitXor => "bitwise XOR",
|
||||
}
|
||||
.to_string()
|
||||
}
|
||||
}
|
||||
|
||||
impl ToString for ExpressionPrefixOpcode {
|
||||
fn to_string(&self) -> String {
|
||||
match self {
|
||||
ExpressionPrefixOpcode::Sub => "additive inverse",
|
||||
ExpressionPrefixOpcode::BoolNot => "boolean NOT",
|
||||
ExpressionPrefixOpcode::Complement => "bitwise complement",
|
||||
}
|
||||
.to_string()
|
||||
}
|
||||
}
|
||||
|
||||
impl ToString for AssignOp {
|
||||
fn to_string(&self) -> String {
|
||||
match self {
|
||||
AssignOp::AssignVar => "assignment to variable",
|
||||
AssignOp::AssignSignal => "assignment to signal",
|
||||
AssignOp::AssignConstraintSignal => "constraint assignment to signal"
|
||||
}
|
||||
.to_string()
|
||||
}
|
||||
}
|
||||
197
program_analysis/src/visitor.rs
Normal file
197
program_analysis/src/visitor.rs
Normal file
@@ -0,0 +1,197 @@
|
||||
use log::debug;
|
||||
use num_bigint_dig::BigInt;
|
||||
use program_structure::program_library::function_data::{FunctionInfo, FunctionData};
|
||||
use program_structure::program_library::template_data::{TemplateInfo, TemplateData};
|
||||
use program_structure::ast::{Access, AssignOp, Expression, ExpressionInfixOpcode, ExpressionPrefixOpcode, Meta, Statement, VariableType};
|
||||
|
||||
use super::utils::ToString;
|
||||
|
||||
pub trait Visitor {
|
||||
fn visit_templates(&mut self, templates: &TemplateInfo) {
|
||||
for (name, template) in templates.iter() {
|
||||
self.visit_template(name, template);
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_template(&mut self, name: &str, template: &TemplateData) {
|
||||
debug!("visiting template '{name}'");
|
||||
self.visit_stmt(template.get_body());
|
||||
}
|
||||
|
||||
fn visit_functions(&mut self, functions: &FunctionInfo) {
|
||||
for (name, function) in functions.iter() {
|
||||
self.visit_function(name, function);
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_function(&mut self, name: &str, function: &FunctionData) {
|
||||
debug!("visiting function '{name}'");
|
||||
self.visit_stmt(function.get_body());
|
||||
}
|
||||
|
||||
fn visit_stmt(&mut self, stmt: &Statement) {
|
||||
debug!("visiting {}", stmt.to_string());
|
||||
match stmt {
|
||||
Statement::IfThenElse { meta, cond, if_case, else_case } =>
|
||||
self.visit_ite(meta, cond, if_case, else_case),
|
||||
Statement::While { meta, cond, stmt } =>
|
||||
self.visit_while(meta, cond, stmt),
|
||||
Statement::Return { meta, value } =>
|
||||
self.visit_return(meta, value),
|
||||
Statement::InitializationBlock { meta, xtype, initializations } =>
|
||||
self.visit_init_block(meta, xtype, initializations),
|
||||
Statement::Declaration { meta, xtype, name, dimensions, is_constant } =>
|
||||
self.visit_declaration(meta, xtype, name, dimensions, is_constant),
|
||||
Statement::Substitution { meta, var, access, op, rhe } =>
|
||||
self.visit_substitution(meta, var, access, op, rhe),
|
||||
Statement::ConstraintEquality { meta, lhe, rhe } =>
|
||||
self.visit_constraint_eq(meta, lhe, rhe),
|
||||
Statement::LogCall { meta, arg } =>
|
||||
self.visit_log_call(meta, arg),
|
||||
Statement::Block { meta, stmts } =>
|
||||
self.visit_block(meta, stmts),
|
||||
Statement::Assert { meta, arg } =>
|
||||
self.visit_assert(meta, arg),
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_expr(&mut self, expr: &Expression) {
|
||||
debug!("visiting {}", expr.to_string());
|
||||
match expr {
|
||||
Expression::InfixOp { meta, lhe, infix_op, rhe } =>
|
||||
self.visit_infix_op(meta, lhe, infix_op, rhe),
|
||||
Expression::PrefixOp { meta, prefix_op, rhe } =>
|
||||
self.visit_prefix_op(meta, prefix_op, rhe),
|
||||
Expression::InlineSwitchOp { meta, cond, if_true, if_false } =>
|
||||
self.visit_inline_switch_op(meta, cond, if_true, if_false),
|
||||
Expression::Variable { meta, name, access } =>
|
||||
self.visit_variable(meta, name, access),
|
||||
Expression::Number(meta, value) =>
|
||||
self.visit_number(meta, value),
|
||||
Expression::Call { meta, id, args } =>
|
||||
self.visit_call(meta, id, args),
|
||||
Expression::ArrayInLine { meta, values } =>
|
||||
self.visit_array_inline(meta, values),
|
||||
}
|
||||
}
|
||||
|
||||
// Statement visitors.
|
||||
fn visit_ite(&mut self, _meta: &Meta, cond: &Expression, if_case: &Statement, else_case: &Option<Box<Statement>>) {
|
||||
// Default implementation does nothing.
|
||||
|
||||
self.visit_expr(cond);
|
||||
self.visit_stmt(if_case);
|
||||
if let Some(else_case) = else_case {
|
||||
self.visit_stmt(else_case);
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_while(&mut self, _meta: &Meta, cond: &Expression, stmt: &Statement) {
|
||||
// Default implementation does nothing.
|
||||
|
||||
self.visit_expr(cond);
|
||||
self.visit_stmt(stmt);
|
||||
}
|
||||
|
||||
fn visit_return(&mut self, _meta: &Meta, value: &Expression) {
|
||||
// Default implementation does nothing.
|
||||
|
||||
self.visit_expr(value);
|
||||
}
|
||||
|
||||
fn visit_init_block(&mut self, _meta: &Meta, _xtype: &VariableType, initializations: &[Statement]) {
|
||||
// Default implementation does nothing.
|
||||
|
||||
for init in initializations {
|
||||
self.visit_stmt(init);
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_declaration(&mut self, _meta: &Meta, _xtype: &VariableType, _name: &str, dimensions: &[Expression], _is_constant: &bool) {
|
||||
// Default implementation does nothing.
|
||||
|
||||
for dim in dimensions {
|
||||
self.visit_expr(dim);
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_substitution(&mut self, _meta: &Meta, _var: &str, _access: &[Access], _op: &AssignOp, rhe: &Expression) {
|
||||
// Default implementation does nothing.
|
||||
|
||||
self.visit_expr(rhe);
|
||||
}
|
||||
|
||||
fn visit_constraint_eq(&mut self, _meta: &Meta, lhe: &Expression, rhe: &Expression) {
|
||||
// Default implementation does nothing.
|
||||
|
||||
self.visit_expr(lhe);
|
||||
self.visit_expr(rhe);
|
||||
}
|
||||
|
||||
fn visit_log_call(&mut self, _meta: &Meta, arg: &Expression) {
|
||||
// Default implementation does nothing.
|
||||
|
||||
self.visit_expr(arg);
|
||||
}
|
||||
|
||||
fn visit_block(&mut self, _meta: &Meta, stmts: &[Statement]) {
|
||||
// Default implementation does nothing.
|
||||
|
||||
for stmt in stmts {
|
||||
self.visit_stmt(stmt);
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_assert(&mut self, _meta: &Meta, arg: &Expression) {
|
||||
// Default implementation does nothing.
|
||||
|
||||
self.visit_expr(arg);
|
||||
}
|
||||
|
||||
// Expression visitors.
|
||||
fn visit_infix_op(&mut self, _meta: &Meta, lhe: &Expression, _op: &ExpressionInfixOpcode, rhe: &Expression) {
|
||||
// Default implementation does nothing.
|
||||
|
||||
self.visit_expr(lhe);
|
||||
self.visit_expr(rhe);
|
||||
}
|
||||
|
||||
fn visit_prefix_op(&mut self, _meta: &Meta, _op: &ExpressionPrefixOpcode, rhe: &Expression) {
|
||||
// Default implementation does nothing.
|
||||
|
||||
self.visit_expr(rhe);
|
||||
}
|
||||
|
||||
fn visit_inline_switch_op(&mut self, _meta: &Meta, cond: &Expression, if_true: &Expression, if_false: &Expression) {
|
||||
// Default implementation does nothing.
|
||||
|
||||
self.visit_expr(cond);
|
||||
self.visit_expr(if_true);
|
||||
self.visit_expr(if_false);
|
||||
}
|
||||
|
||||
fn visit_variable(&mut self, _meta: &Meta, _name: &str, _access: &[Access]) {
|
||||
// Default implementation does nothing.
|
||||
}
|
||||
|
||||
fn visit_number(&mut self, _meta: &Meta, _value: &BigInt) {
|
||||
// Default implementation does nothing.
|
||||
}
|
||||
|
||||
fn visit_call(&mut self, _meta: &Meta, _id: &str, args: &[Expression]) {
|
||||
// Default implementation does nothing.
|
||||
|
||||
for arg in args {
|
||||
self.visit_expr(arg);
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_array_inline(&mut self, _meta: &Meta, values: &[Expression]) {
|
||||
// Default implementation does nothing.
|
||||
|
||||
for value in values {
|
||||
self.visit_expr(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
17
program_structure/Cargo.toml
Normal file
17
program_structure/Cargo.toml
Normal file
@@ -0,0 +1,17 @@
|
||||
[package]
|
||||
name = "program_structure"
|
||||
version = "2.0.1"
|
||||
authors = ["hermeGarcia <hermegarcianavarro@gmail.com>"]
|
||||
edition = "2018"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
codespan = "0.9.0"
|
||||
codespan-reporting = "0.9.0"
|
||||
regex = "1.1.2"
|
||||
rustc-hex = "2.0.1"
|
||||
num-bigint-dig = "0.6.0"
|
||||
num-traits = "0.2.6"
|
||||
serde = "1.0.82"
|
||||
serde_derive = "1.0.91"
|
||||
11
program_structure/src/abstract_syntax_tree/assign_op_impl.rs
Normal file
11
program_structure/src/abstract_syntax_tree/assign_op_impl.rs
Normal file
@@ -0,0 +1,11 @@
|
||||
use super::ast::AssignOp;
|
||||
|
||||
impl AssignOp {
|
||||
pub fn is_signal_operator(self) -> bool {
|
||||
use AssignOp::*;
|
||||
match self {
|
||||
AssignConstraintSignal | AssignSignal => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
383
program_structure/src/abstract_syntax_tree/ast.rs
Normal file
383
program_structure/src/abstract_syntax_tree/ast.rs
Normal file
@@ -0,0 +1,383 @@
|
||||
use crate::file_definition::FileLocation;
|
||||
use num_bigint::BigInt;
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
|
||||
pub trait FillMeta {
|
||||
fn fill(&mut self, file_id: usize, elem_id: &mut usize);
|
||||
}
|
||||
|
||||
pub type MainComponent = (Vec<String>, Expression);
|
||||
pub fn build_main_component(public: Vec<String>, call: Expression) -> MainComponent {
|
||||
(public, call)
|
||||
}
|
||||
|
||||
|
||||
pub type Version = (usize, usize, usize);
|
||||
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Meta {
|
||||
pub elem_id: usize,
|
||||
pub start: usize,
|
||||
pub end: usize,
|
||||
pub location: FileLocation,
|
||||
pub file_id: Option<usize>,
|
||||
pub component_inference: Option<String>,
|
||||
type_knowledge: TypeKnowledge,
|
||||
memory_knowledge: MemoryKnowledge,
|
||||
}
|
||||
impl Meta {
|
||||
pub fn new(start: usize, end: usize) -> Meta {
|
||||
Meta {
|
||||
end,
|
||||
start,
|
||||
elem_id: 0,
|
||||
location: start..end,
|
||||
file_id: Option::None,
|
||||
component_inference: None,
|
||||
type_knowledge: TypeKnowledge::default(),
|
||||
memory_knowledge: MemoryKnowledge::default(),
|
||||
}
|
||||
}
|
||||
pub fn change_location(&mut self, location: FileLocation, file_id: Option<usize>) {
|
||||
self.location = location;
|
||||
self.file_id = file_id;
|
||||
}
|
||||
pub fn get_start(&self) -> usize {
|
||||
self.location.start
|
||||
}
|
||||
pub fn get_end(&self) -> usize {
|
||||
self.location.end
|
||||
}
|
||||
pub fn get_file_id(&self) -> usize {
|
||||
if let Option::Some(id) = self.file_id {
|
||||
id
|
||||
} else {
|
||||
panic!("Empty file id accessed")
|
||||
}
|
||||
}
|
||||
pub fn get_memory_knowledge(&self) -> &MemoryKnowledge {
|
||||
&self.memory_knowledge
|
||||
}
|
||||
pub fn get_type_knowledge(&self) -> &TypeKnowledge {
|
||||
&self.type_knowledge
|
||||
}
|
||||
pub fn get_mut_memory_knowledge(&mut self) -> &mut MemoryKnowledge {
|
||||
&mut self.memory_knowledge
|
||||
}
|
||||
pub fn get_mut_type_knowledge(&mut self) -> &mut TypeKnowledge {
|
||||
&mut self.type_knowledge
|
||||
}
|
||||
pub fn file_location(&self) -> FileLocation {
|
||||
self.location.clone()
|
||||
}
|
||||
pub fn set_file_id(&mut self, file_id: usize) {
|
||||
self.file_id = Option::Some(file_id);
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct AST {
|
||||
pub meta: Meta,
|
||||
pub compiler_version: Option<Version>,
|
||||
pub includes: Vec<String>,
|
||||
pub definitions: Vec<Definition>,
|
||||
pub main_component: Option<MainComponent>,
|
||||
}
|
||||
impl AST {
|
||||
pub fn new(
|
||||
meta: Meta,
|
||||
compiler_version: Option<Version>,
|
||||
includes: Vec<String>,
|
||||
definitions: Vec<Definition>,
|
||||
main_component: Option<MainComponent>,
|
||||
) -> AST {
|
||||
AST { meta, compiler_version, includes, definitions, main_component }
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub enum Definition {
|
||||
Template {
|
||||
meta: Meta,
|
||||
name: String,
|
||||
args: Vec<String>,
|
||||
arg_location: FileLocation,
|
||||
body: Statement,
|
||||
parallel: bool,
|
||||
},
|
||||
Function {
|
||||
meta: Meta,
|
||||
name: String,
|
||||
args: Vec<String>,
|
||||
arg_location: FileLocation,
|
||||
body: Statement,
|
||||
},
|
||||
}
|
||||
pub fn build_template(
|
||||
meta: Meta,
|
||||
name: String,
|
||||
args: Vec<String>,
|
||||
arg_location: FileLocation,
|
||||
body: Statement,
|
||||
parallel: bool,
|
||||
) -> Definition {
|
||||
Definition::Template {
|
||||
meta,
|
||||
name,
|
||||
args,
|
||||
arg_location,
|
||||
body,
|
||||
parallel,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn build_function(
|
||||
meta: Meta,
|
||||
name: String,
|
||||
args: Vec<String>,
|
||||
arg_location: FileLocation,
|
||||
body: Statement,
|
||||
) -> Definition {
|
||||
Definition::Function { meta, name, args, arg_location, body }
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub enum Statement {
|
||||
IfThenElse {
|
||||
meta: Meta,
|
||||
cond: Expression,
|
||||
if_case: Box<Statement>,
|
||||
else_case: Option<Box<Statement>>,
|
||||
},
|
||||
While {
|
||||
meta: Meta,
|
||||
cond: Expression,
|
||||
stmt: Box<Statement>,
|
||||
},
|
||||
Return {
|
||||
meta: Meta,
|
||||
value: Expression,
|
||||
},
|
||||
InitializationBlock {
|
||||
meta: Meta,
|
||||
xtype: VariableType,
|
||||
initializations: Vec<Statement>,
|
||||
},
|
||||
Declaration {
|
||||
meta: Meta,
|
||||
xtype: VariableType,
|
||||
name: String,
|
||||
dimensions: Vec<Expression>,
|
||||
is_constant: bool,
|
||||
},
|
||||
Substitution {
|
||||
meta: Meta,
|
||||
var: String,
|
||||
access: Vec<Access>,
|
||||
op: AssignOp,
|
||||
rhe: Expression,
|
||||
},
|
||||
ConstraintEquality {
|
||||
meta: Meta,
|
||||
lhe: Expression,
|
||||
rhe: Expression,
|
||||
},
|
||||
LogCall {
|
||||
meta: Meta,
|
||||
arg: Expression,
|
||||
},
|
||||
Block {
|
||||
meta: Meta,
|
||||
stmts: Vec<Statement>,
|
||||
},
|
||||
Assert {
|
||||
meta: Meta,
|
||||
arg: Expression,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
|
||||
pub enum SignalElementType {
|
||||
Empty,
|
||||
Binary,
|
||||
FieldElement,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
|
||||
pub enum SignalType {
|
||||
Output,
|
||||
Input,
|
||||
Intermediate,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Ord, PartialOrd, Eq)]
|
||||
pub enum VariableType {
|
||||
Var,
|
||||
Signal(SignalType, SignalElementType),
|
||||
Component,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub enum Expression {
|
||||
InfixOp {
|
||||
meta: Meta,
|
||||
lhe: Box<Expression>,
|
||||
infix_op: ExpressionInfixOpcode,
|
||||
rhe: Box<Expression>,
|
||||
},
|
||||
PrefixOp {
|
||||
meta: Meta,
|
||||
prefix_op: ExpressionPrefixOpcode,
|
||||
rhe: Box<Expression>,
|
||||
},
|
||||
InlineSwitchOp {
|
||||
meta: Meta,
|
||||
cond: Box<Expression>,
|
||||
if_true: Box<Expression>,
|
||||
if_false: Box<Expression>,
|
||||
},
|
||||
Variable {
|
||||
meta: Meta,
|
||||
name: String,
|
||||
access: Vec<Access>,
|
||||
},
|
||||
Number(Meta, BigInt),
|
||||
Call {
|
||||
meta: Meta,
|
||||
id: String,
|
||||
args: Vec<Expression>,
|
||||
},
|
||||
ArrayInLine {
|
||||
meta: Meta,
|
||||
values: Vec<Expression>,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub enum Access {
|
||||
ComponentAccess(String),
|
||||
ArrayAccess(Expression),
|
||||
}
|
||||
pub fn build_component_access(acc: String) -> Access {
|
||||
Access::ComponentAccess(acc)
|
||||
}
|
||||
pub fn build_array_access(expr: Expression) -> Access {
|
||||
Access::ArrayAccess(expr)
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Eq, PartialEq)]
|
||||
pub enum AssignOp {
|
||||
AssignVar,
|
||||
AssignSignal,
|
||||
AssignConstraintSignal,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, PartialEq)]
|
||||
pub enum ExpressionInfixOpcode {
|
||||
Mul,
|
||||
Div,
|
||||
Add,
|
||||
Sub,
|
||||
Pow,
|
||||
IntDiv,
|
||||
Mod,
|
||||
ShiftL,
|
||||
ShiftR,
|
||||
LesserEq,
|
||||
GreaterEq,
|
||||
Lesser,
|
||||
Greater,
|
||||
Eq,
|
||||
NotEq,
|
||||
BoolOr,
|
||||
BoolAnd,
|
||||
BitOr,
|
||||
BitAnd,
|
||||
BitXor,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, PartialEq)]
|
||||
pub enum ExpressionPrefixOpcode {
|
||||
Sub,
|
||||
BoolNot,
|
||||
Complement,
|
||||
}
|
||||
|
||||
// Knowledge buckets
|
||||
|
||||
#[derive(Copy, Clone, PartialOrd, PartialEq, Ord, Eq)]
|
||||
pub enum TypeReduction {
|
||||
Variable,
|
||||
Component,
|
||||
Signal,
|
||||
}
|
||||
|
||||
#[derive(Default, Clone)]
|
||||
pub struct TypeKnowledge {
|
||||
reduces_to: Option<TypeReduction>,
|
||||
}
|
||||
impl TypeKnowledge {
|
||||
pub fn new() -> TypeKnowledge {
|
||||
TypeKnowledge::default()
|
||||
}
|
||||
pub fn set_reduces_to(&mut self, reduces_to: TypeReduction) {
|
||||
self.reduces_to = Option::Some(reduces_to);
|
||||
}
|
||||
pub fn get_reduces_to(&self) -> TypeReduction {
|
||||
if let Option::Some(t) = &self.reduces_to {
|
||||
*t
|
||||
} else {
|
||||
panic!("reduces_to knowledge is been look at without being initialized");
|
||||
}
|
||||
}
|
||||
pub fn is_var(&self) -> bool {
|
||||
self.get_reduces_to() == TypeReduction::Variable
|
||||
}
|
||||
pub fn is_component(&self) -> bool {
|
||||
self.get_reduces_to() == TypeReduction::Component
|
||||
}
|
||||
pub fn is_signal(&self) -> bool {
|
||||
self.get_reduces_to() == TypeReduction::Signal
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default, Clone)]
|
||||
pub struct MemoryKnowledge {
|
||||
concrete_dimensions: Option<Vec<usize>>,
|
||||
full_length: Option<usize>,
|
||||
abstract_memory_address: Option<usize>,
|
||||
}
|
||||
impl MemoryKnowledge {
|
||||
pub fn new() -> MemoryKnowledge {
|
||||
MemoryKnowledge::default()
|
||||
}
|
||||
pub fn set_concrete_dimensions(&mut self, value: Vec<usize>) {
|
||||
self.full_length = Option::Some(value.iter().fold(1, |p, v| p * (*v)));
|
||||
self.concrete_dimensions = Option::Some(value);
|
||||
}
|
||||
pub fn set_abstract_memory_address(&mut self, value: usize) {
|
||||
self.abstract_memory_address = Option::Some(value);
|
||||
}
|
||||
pub fn get_concrete_dimensions(&self) -> &[usize] {
|
||||
if let Option::Some(v) = &self.concrete_dimensions {
|
||||
v
|
||||
} else {
|
||||
panic!("concrete dimensions was look at without being initialized");
|
||||
}
|
||||
}
|
||||
pub fn get_full_length(&self) -> usize {
|
||||
if let Option::Some(v) = &self.full_length {
|
||||
*v
|
||||
} else {
|
||||
panic!("full dimension was look at without being initialized");
|
||||
}
|
||||
}
|
||||
pub fn get_abstract_memory_address(&self) -> usize {
|
||||
if let Option::Some(v) = &self.abstract_memory_address {
|
||||
*v
|
||||
} else {
|
||||
panic!("abstract memory address was look at without being initialized");
|
||||
}
|
||||
}
|
||||
}
|
||||
18
program_structure/src/abstract_syntax_tree/ast_impl.rs
Normal file
18
program_structure/src/abstract_syntax_tree/ast_impl.rs
Normal file
@@ -0,0 +1,18 @@
|
||||
use super::ast::*;
|
||||
|
||||
impl AST {
|
||||
pub fn get_includes(&self) -> &Vec<String> {
|
||||
&self.includes
|
||||
}
|
||||
|
||||
pub fn get_version(&self) -> &Option<Version> {
|
||||
&self.compiler_version
|
||||
}
|
||||
|
||||
pub fn get_definitions(&self) -> &Vec<Definition> {
|
||||
&self.definitions
|
||||
}
|
||||
pub fn decompose(self) -> (Meta, Option<Version>, Vec<String>, Vec<Definition>, Option<MainComponent>) {
|
||||
(self.meta, self.compiler_version, self.includes, self.definitions, self.main_component)
|
||||
}
|
||||
}
|
||||
71
program_structure/src/abstract_syntax_tree/ast_shortcuts.rs
Normal file
71
program_structure/src/abstract_syntax_tree/ast_shortcuts.rs
Normal file
@@ -0,0 +1,71 @@
|
||||
use super::ast::*;
|
||||
use super::expression_builders::*;
|
||||
use super::statement_builders::*;
|
||||
use crate::ast::{Access, Expression, VariableType};
|
||||
use num_bigint::BigInt;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Symbol {
|
||||
pub name: String,
|
||||
pub is_array: Vec<Expression>,
|
||||
pub init: Option<Expression>,
|
||||
}
|
||||
|
||||
pub fn assign_with_op_shortcut(
|
||||
op: ExpressionInfixOpcode,
|
||||
meta: Meta,
|
||||
variable: (String, Vec<Access>),
|
||||
rhe: Expression,
|
||||
) -> Statement {
|
||||
let (var, access) = variable;
|
||||
let variable = build_variable(meta.clone(), var.clone(), access.clone());
|
||||
let infix = build_infix(meta.clone(), variable, op, rhe);
|
||||
build_substitution(meta, var, access, AssignOp::AssignVar, infix)
|
||||
}
|
||||
|
||||
pub fn plusplus(meta: Meta, variable: (String, Vec<Access>)) -> Statement {
|
||||
let one = build_number(meta.clone(), BigInt::from(1));
|
||||
assign_with_op_shortcut(ExpressionInfixOpcode::Add, meta, variable, one)
|
||||
}
|
||||
|
||||
pub fn subsub(meta: Meta, variable: (String, Vec<Access>)) -> Statement {
|
||||
let one = build_number(meta.clone(), BigInt::from(1));
|
||||
assign_with_op_shortcut(ExpressionInfixOpcode::Sub, meta, variable, one)
|
||||
}
|
||||
|
||||
pub fn for_into_while(
|
||||
meta: Meta,
|
||||
init: Statement,
|
||||
cond: Expression,
|
||||
step: Statement,
|
||||
body: Statement,
|
||||
) -> Statement {
|
||||
let while_body = build_block(body.get_meta().clone(), vec![body, step]);
|
||||
let while_statement = build_while_block(meta.clone(), cond, while_body);
|
||||
build_block(meta, vec![init, while_statement])
|
||||
}
|
||||
|
||||
pub fn split_declaration_into_single_nodes(
|
||||
meta: Meta,
|
||||
xtype: VariableType,
|
||||
symbols: Vec<Symbol>,
|
||||
) -> Statement {
|
||||
let mut initializations = Vec::new();
|
||||
|
||||
for symbol in symbols {
|
||||
let with_meta = meta.clone();
|
||||
let has_type = xtype;
|
||||
let name = symbol.name.clone();
|
||||
let dimensions = symbol.is_array;
|
||||
let possible_init = symbol.init;
|
||||
let single_declaration = build_declaration(with_meta, has_type, name, dimensions);
|
||||
initializations.push(single_declaration);
|
||||
|
||||
if let Option::Some(init) = possible_init {
|
||||
let substitution =
|
||||
build_substitution(meta.clone(), symbol.name, vec![], AssignOp::AssignVar, init);
|
||||
initializations.push(substitution);
|
||||
}
|
||||
}
|
||||
build_initialization_block(meta, xtype, initializations)
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
use super::ast::*;
|
||||
use num_bigint::BigInt;
|
||||
use Expression::*;
|
||||
|
||||
pub fn build_infix(
|
||||
meta: Meta,
|
||||
lhe: Expression,
|
||||
infix_op: ExpressionInfixOpcode,
|
||||
rhe: Expression,
|
||||
) -> Expression {
|
||||
InfixOp { meta, infix_op, lhe: Box::new(lhe), rhe: Box::new(rhe) }
|
||||
}
|
||||
|
||||
pub fn build_prefix(meta: Meta, prefix_op: ExpressionPrefixOpcode, rhe: Expression) -> Expression {
|
||||
PrefixOp { meta, prefix_op, rhe: Box::new(rhe) }
|
||||
}
|
||||
|
||||
pub fn build_inline_switch_op(
|
||||
meta: Meta,
|
||||
cond: Expression,
|
||||
if_true: Expression,
|
||||
if_false: Expression,
|
||||
) -> Expression {
|
||||
InlineSwitchOp {
|
||||
meta,
|
||||
cond: Box::new(cond),
|
||||
if_true: Box::new(if_true),
|
||||
if_false: Box::new(if_false),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn build_variable(meta: Meta, name: String, access: Vec<Access>) -> Expression {
|
||||
Variable { meta, name, access }
|
||||
}
|
||||
|
||||
pub fn build_number(meta: Meta, value: BigInt) -> Expression {
|
||||
Expression::Number(meta, value)
|
||||
}
|
||||
|
||||
pub fn build_call(meta: Meta, id: String, args: Vec<Expression>) -> Expression {
|
||||
Call { meta, id, args }
|
||||
}
|
||||
|
||||
pub fn build_array_in_line(meta: Meta, values: Vec<Expression>) -> Expression {
|
||||
ArrayInLine { meta, values }
|
||||
}
|
||||
175
program_structure/src/abstract_syntax_tree/expression_impl.rs
Normal file
175
program_structure/src/abstract_syntax_tree/expression_impl.rs
Normal file
@@ -0,0 +1,175 @@
|
||||
use super::ast::*;
|
||||
|
||||
impl Expression {
|
||||
pub fn get_meta(&self) -> &Meta {
|
||||
use Expression::*;
|
||||
match self {
|
||||
InfixOp { meta, .. }
|
||||
| PrefixOp { meta, .. }
|
||||
| InlineSwitchOp { meta, .. }
|
||||
| Variable { meta, .. }
|
||||
| Number(meta, ..)
|
||||
| Call { meta, .. }
|
||||
| ArrayInLine { meta, .. } => meta,
|
||||
}
|
||||
}
|
||||
pub fn get_mut_meta(&mut self) -> &mut Meta {
|
||||
use Expression::*;
|
||||
match self {
|
||||
InfixOp { meta, .. }
|
||||
| PrefixOp { meta, .. }
|
||||
| InlineSwitchOp { meta, .. }
|
||||
| Variable { meta, .. }
|
||||
| Number(meta, ..)
|
||||
| Call { meta, .. }
|
||||
| ArrayInLine { meta, .. } => meta,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_array(&self) -> bool {
|
||||
use Expression::*;
|
||||
if let ArrayInLine { .. } = self {
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_infix(&self) -> bool {
|
||||
use Expression::*;
|
||||
if let InfixOp { .. } = self {
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_prefix(&self) -> bool {
|
||||
use Expression::*;
|
||||
if let PrefixOp { .. } = self {
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_switch(&self) -> bool {
|
||||
use Expression::*;
|
||||
if let InlineSwitchOp { .. } = self {
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_variable(&self) -> bool {
|
||||
use Expression::*;
|
||||
if let Variable { .. } = self {
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_number(&self) -> bool {
|
||||
use Expression::*;
|
||||
if let Number(..) = self {
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_call(&self) -> bool {
|
||||
use Expression::*;
|
||||
if let Call { .. } = self {
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl FillMeta for Expression {
|
||||
fn fill(&mut self, file_id: usize, element_id: &mut usize) {
|
||||
use Expression::*;
|
||||
self.get_mut_meta().elem_id = *element_id;
|
||||
*element_id += 1;
|
||||
match self {
|
||||
Number(meta, _) => fill_number(meta, file_id, element_id),
|
||||
Variable { meta, access, .. } => fill_variable(meta, access, file_id, element_id),
|
||||
InfixOp { meta, lhe, rhe, .. } => fill_infix(meta, lhe, rhe, file_id, element_id),
|
||||
PrefixOp { meta, rhe, .. } => fill_prefix(meta, rhe, file_id, element_id),
|
||||
InlineSwitchOp { meta, cond, if_false, if_true, .. } => {
|
||||
fill_inline_switch_op(meta, cond, if_true, if_false, file_id, element_id)
|
||||
}
|
||||
Call { meta, args, .. } => fill_call(meta, args, file_id, element_id),
|
||||
ArrayInLine { meta, values, .. } => {
|
||||
fill_array_inline(meta, values, file_id, element_id)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn fill_number(meta: &mut Meta, file_id: usize, _element_id: &mut usize) {
|
||||
meta.set_file_id(file_id);
|
||||
}
|
||||
|
||||
fn fill_variable(meta: &mut Meta, access: &mut [Access], file_id: usize, element_id: &mut usize) {
|
||||
meta.set_file_id(file_id);
|
||||
for acc in access {
|
||||
if let Access::ArrayAccess(e) = acc {
|
||||
e.fill(file_id, element_id)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn fill_infix(
|
||||
meta: &mut Meta,
|
||||
lhe: &mut Expression,
|
||||
rhe: &mut Expression,
|
||||
file_id: usize,
|
||||
element_id: &mut usize,
|
||||
) {
|
||||
meta.set_file_id(file_id);
|
||||
lhe.fill(file_id, element_id);
|
||||
rhe.fill(file_id, element_id);
|
||||
}
|
||||
|
||||
fn fill_prefix(meta: &mut Meta, rhe: &mut Expression, file_id: usize, element_id: &mut usize) {
|
||||
meta.set_file_id(file_id);
|
||||
rhe.fill(file_id, element_id);
|
||||
}
|
||||
|
||||
fn fill_inline_switch_op(
|
||||
meta: &mut Meta,
|
||||
cond: &mut Expression,
|
||||
if_true: &mut Expression,
|
||||
if_false: &mut Expression,
|
||||
file_id: usize,
|
||||
element_id: &mut usize,
|
||||
) {
|
||||
meta.set_file_id(file_id);
|
||||
cond.fill(file_id, element_id);
|
||||
if_true.fill(file_id, element_id);
|
||||
if_false.fill(file_id, element_id);
|
||||
}
|
||||
|
||||
fn fill_call(meta: &mut Meta, args: &mut [Expression], file_id: usize, element_id: &mut usize) {
|
||||
meta.set_file_id(file_id);
|
||||
for a in args {
|
||||
a.fill(file_id, element_id);
|
||||
}
|
||||
}
|
||||
|
||||
fn fill_array_inline(
|
||||
meta: &mut Meta,
|
||||
values: &mut [Expression],
|
||||
file_id: usize,
|
||||
element_id: &mut usize,
|
||||
) {
|
||||
meta.set_file_id(file_id);
|
||||
for v in values {
|
||||
v.fill(file_id, element_id);
|
||||
}
|
||||
}
|
||||
8
program_structure/src/abstract_syntax_tree/mod.rs
Normal file
8
program_structure/src/abstract_syntax_tree/mod.rs
Normal file
@@ -0,0 +1,8 @@
|
||||
mod assign_op_impl;
|
||||
pub mod ast;
|
||||
mod ast_impl;
|
||||
pub mod ast_shortcuts;
|
||||
pub mod expression_builders;
|
||||
mod expression_impl;
|
||||
pub mod statement_builders;
|
||||
mod statement_impl;
|
||||
@@ -0,0 +1,62 @@
|
||||
use super::ast::*;
|
||||
use Statement::*;
|
||||
|
||||
pub fn build_conditional_block(
|
||||
meta: Meta,
|
||||
cond: Expression,
|
||||
if_case: Statement,
|
||||
else_case: Option<Statement>,
|
||||
) -> Statement {
|
||||
IfThenElse { meta, cond, else_case: else_case.map(|s| Box::new(s)), if_case: Box::new(if_case) }
|
||||
}
|
||||
|
||||
pub fn build_while_block(meta: Meta, cond: Expression, stmt: Statement) -> Statement {
|
||||
While { meta, cond, stmt: Box::new(stmt) }
|
||||
}
|
||||
|
||||
pub fn build_initialization_block(
|
||||
meta: Meta,
|
||||
xtype: VariableType,
|
||||
initializations: Vec<Statement>,
|
||||
) -> Statement {
|
||||
InitializationBlock { meta, xtype, initializations }
|
||||
}
|
||||
pub fn build_block(meta: Meta, stmts: Vec<Statement>) -> Statement {
|
||||
Block { meta, stmts }
|
||||
}
|
||||
|
||||
pub fn build_return(meta: Meta, value: Expression) -> Statement {
|
||||
Return { meta, value }
|
||||
}
|
||||
|
||||
pub fn build_declaration(
|
||||
meta: Meta,
|
||||
xtype: VariableType,
|
||||
name: String,
|
||||
dimensions: Vec<Expression>,
|
||||
) -> Statement {
|
||||
let is_constant = true;
|
||||
Declaration { meta, xtype, name, dimensions, is_constant }
|
||||
}
|
||||
|
||||
pub fn build_substitution(
|
||||
meta: Meta,
|
||||
var: String,
|
||||
access: Vec<Access>,
|
||||
op: AssignOp,
|
||||
rhe: Expression,
|
||||
) -> Statement {
|
||||
Substitution { meta, var, access, op, rhe }
|
||||
}
|
||||
|
||||
pub fn build_constraint_equality(meta: Meta, lhe: Expression, rhe: Expression) -> Statement {
|
||||
ConstraintEquality { meta, lhe, rhe }
|
||||
}
|
||||
|
||||
pub fn build_log_call(meta: Meta, arg: Expression) -> Statement {
|
||||
LogCall { meta, arg }
|
||||
}
|
||||
|
||||
pub fn build_assert(meta: Meta, arg: Expression) -> Statement {
|
||||
Assert { meta, arg }
|
||||
}
|
||||
247
program_structure/src/abstract_syntax_tree/statement_impl.rs
Normal file
247
program_structure/src/abstract_syntax_tree/statement_impl.rs
Normal file
@@ -0,0 +1,247 @@
|
||||
use super::ast::*;
|
||||
|
||||
impl Statement {
|
||||
pub fn get_meta(&self) -> &Meta {
|
||||
use Statement::*;
|
||||
match self {
|
||||
IfThenElse { meta, .. }
|
||||
| While { meta, .. }
|
||||
| Return { meta, .. }
|
||||
| Declaration { meta, .. }
|
||||
| Substitution { meta, .. }
|
||||
| LogCall { meta, .. }
|
||||
| Block { meta, .. }
|
||||
| Assert { meta, .. }
|
||||
| ConstraintEquality { meta, .. }
|
||||
| InitializationBlock { meta, .. } => meta,
|
||||
}
|
||||
}
|
||||
pub fn get_mut_meta(&mut self) -> &mut Meta {
|
||||
use Statement::*;
|
||||
match self {
|
||||
IfThenElse { meta, .. }
|
||||
| While { meta, .. }
|
||||
| Return { meta, .. }
|
||||
| Declaration { meta, .. }
|
||||
| Substitution { meta, .. }
|
||||
| LogCall { meta, .. }
|
||||
| Block { meta, .. }
|
||||
| Assert { meta, .. }
|
||||
| ConstraintEquality { meta, .. }
|
||||
| InitializationBlock { meta, .. } => meta,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_if_then_else(&self) -> bool {
|
||||
use Statement::IfThenElse;
|
||||
if let IfThenElse { .. } = self {
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
pub fn is_while(&self) -> bool {
|
||||
use Statement::While;
|
||||
if let While { .. } = self {
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
pub fn is_return(&self) -> bool {
|
||||
use Statement::Return;
|
||||
if let Return { .. } = self {
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
pub fn is_initialization_block(&self) -> bool {
|
||||
use Statement::InitializationBlock;
|
||||
if let InitializationBlock { .. } = self {
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
pub fn is_declaration(&self) -> bool {
|
||||
use Statement::Declaration;
|
||||
if let Declaration { .. } = self {
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
pub fn is_substitution(&self) -> bool {
|
||||
use Statement::Substitution;
|
||||
if let Substitution { .. } = self {
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
pub fn is_constraint_equality(&self) -> bool {
|
||||
use Statement::ConstraintEquality;
|
||||
if let ConstraintEquality { .. } = self {
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
pub fn is_log_call(&self) -> bool {
|
||||
use Statement::LogCall;
|
||||
if let LogCall { .. } = self {
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
pub fn is_block(&self) -> bool {
|
||||
use Statement::Block;
|
||||
if let Block { .. } = self {
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
pub fn is_assert(&self) -> bool {
|
||||
use Statement::Assert;
|
||||
if let Assert { .. } = self {
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl FillMeta for Statement {
|
||||
fn fill(&mut self, file_id: usize, element_id: &mut usize) {
|
||||
use Statement::*;
|
||||
self.get_mut_meta().elem_id = *element_id;
|
||||
*element_id += 1;
|
||||
match self {
|
||||
IfThenElse { meta, cond, if_case, else_case, .. } => {
|
||||
fill_conditional(meta, cond, if_case, else_case, file_id, element_id)
|
||||
}
|
||||
While { meta, cond, stmt } => fill_while(meta, cond, stmt, file_id, element_id),
|
||||
Return { meta, value } => fill_return(meta, value, file_id, element_id),
|
||||
InitializationBlock { meta, initializations, .. } => {
|
||||
fill_initialization(meta, initializations, file_id, element_id)
|
||||
}
|
||||
Declaration { meta, dimensions, .. } => {
|
||||
fill_declaration(meta, dimensions, file_id, element_id)
|
||||
}
|
||||
Substitution { meta, access, rhe, .. } => {
|
||||
fill_substitution(meta, access, rhe, file_id, element_id)
|
||||
}
|
||||
ConstraintEquality { meta, lhe, rhe } => {
|
||||
fill_constraint_equality(meta, lhe, rhe, file_id, element_id)
|
||||
}
|
||||
LogCall { meta, arg, .. } => fill_log_call(meta, arg, file_id, element_id),
|
||||
Block { meta, stmts, .. } => fill_block(meta, stmts, file_id, element_id),
|
||||
Assert { meta, arg, .. } => fill_assert(meta, arg, file_id, element_id),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn fill_conditional(
|
||||
meta: &mut Meta,
|
||||
cond: &mut Expression,
|
||||
if_case: &mut Statement,
|
||||
else_case: &mut Option<Box<Statement>>,
|
||||
file_id: usize,
|
||||
element_id: &mut usize,
|
||||
) {
|
||||
meta.set_file_id(file_id);
|
||||
cond.fill(file_id, element_id);
|
||||
if_case.fill(file_id, element_id);
|
||||
if let Option::Some(s) = else_case {
|
||||
s.fill(file_id, element_id);
|
||||
}
|
||||
}
|
||||
|
||||
fn fill_while(
|
||||
meta: &mut Meta,
|
||||
cond: &mut Expression,
|
||||
stmt: &mut Statement,
|
||||
file_id: usize,
|
||||
element_id: &mut usize,
|
||||
) {
|
||||
meta.set_file_id(file_id);
|
||||
cond.fill(file_id, element_id);
|
||||
stmt.fill(file_id, element_id);
|
||||
}
|
||||
|
||||
fn fill_return(meta: &mut Meta, value: &mut Expression, file_id: usize, element_id: &mut usize) {
|
||||
meta.set_file_id(file_id);
|
||||
value.fill(file_id, element_id);
|
||||
}
|
||||
|
||||
fn fill_initialization(
|
||||
meta: &mut Meta,
|
||||
initializations: &mut [Statement],
|
||||
file_id: usize,
|
||||
element_id: &mut usize,
|
||||
) {
|
||||
meta.set_file_id(file_id);
|
||||
for init in initializations {
|
||||
init.fill(file_id, element_id);
|
||||
}
|
||||
}
|
||||
|
||||
fn fill_declaration(
|
||||
meta: &mut Meta,
|
||||
dimensions: &mut [Expression],
|
||||
file_id: usize,
|
||||
element_id: &mut usize,
|
||||
) {
|
||||
meta.set_file_id(file_id);
|
||||
for d in dimensions {
|
||||
d.fill(file_id, element_id);
|
||||
}
|
||||
}
|
||||
|
||||
fn fill_substitution(
|
||||
meta: &mut Meta,
|
||||
access: &mut [Access],
|
||||
rhe: &mut Expression,
|
||||
file_id: usize,
|
||||
element_id: &mut usize,
|
||||
) {
|
||||
meta.set_file_id(file_id);
|
||||
rhe.fill(file_id, element_id);
|
||||
for a in access {
|
||||
if let Access::ArrayAccess(e) = a {
|
||||
e.fill(file_id, element_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn fill_constraint_equality(
|
||||
meta: &mut Meta,
|
||||
lhe: &mut Expression,
|
||||
rhe: &mut Expression,
|
||||
file_id: usize,
|
||||
element_id: &mut usize,
|
||||
) {
|
||||
meta.set_file_id(file_id);
|
||||
lhe.fill(file_id, element_id);
|
||||
rhe.fill(file_id, element_id);
|
||||
}
|
||||
|
||||
fn fill_log_call(meta: &mut Meta, arg: &mut Expression, file_id: usize, element_id: &mut usize) {
|
||||
meta.set_file_id(file_id);
|
||||
arg.fill(file_id, element_id);
|
||||
}
|
||||
|
||||
fn fill_block(meta: &mut Meta, stmts: &mut [Statement], file_id: usize, element_id: &mut usize) {
|
||||
meta.set_file_id(file_id);
|
||||
for s in stmts {
|
||||
s.fill(file_id, element_id);
|
||||
}
|
||||
}
|
||||
|
||||
fn fill_assert(meta: &mut Meta, arg: &mut Expression, file_id: usize, element_id: &mut usize) {
|
||||
meta.set_file_id(file_id);
|
||||
arg.fill(file_id, element_id);
|
||||
}
|
||||
11
program_structure/src/lib.rs
Normal file
11
program_structure/src/lib.rs
Normal file
@@ -0,0 +1,11 @@
|
||||
extern crate num_bigint_dig as num_bigint;
|
||||
extern crate num_traits;
|
||||
|
||||
pub mod abstract_syntax_tree;
|
||||
pub mod program_library;
|
||||
pub mod utils;
|
||||
|
||||
// Library interface
|
||||
pub use abstract_syntax_tree::*;
|
||||
pub use program_library::*;
|
||||
pub use utils::*;
|
||||
148
program_structure/src/program_library/error_code.rs
Normal file
148
program_structure/src/program_library/error_code.rs
Normal file
@@ -0,0 +1,148 @@
|
||||
use core::fmt;
|
||||
use std::fmt::Formatter;
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
pub enum ReportCode {
|
||||
AssertWrongType,
|
||||
ParseFail,
|
||||
CompilerVersionError,
|
||||
WrongTypesInAssignOperation,
|
||||
WrongNumberOfArguments(usize, usize),
|
||||
UndefinedFunction,
|
||||
UndefinedTemplate,
|
||||
UninitializedSymbolInExpression,
|
||||
UnableToTypeFunction,
|
||||
UnreachableConstraints,
|
||||
UnknownIndex,
|
||||
UnknownDimension,
|
||||
SameFunctionDeclaredTwice,
|
||||
SameTemplateDeclaredTwice,
|
||||
SameSymbolDeclaredTwice,
|
||||
StaticInfoWasOverwritten,
|
||||
SignalInLineInitialization,
|
||||
SignalOutsideOriginalScope,
|
||||
FunctionWrongNumberOfArguments,
|
||||
FunctionInconsistentTyping,
|
||||
FunctionPathWithoutReturn,
|
||||
FunctionReturnError,
|
||||
ForbiddenDeclarationInFunction,
|
||||
NonHomogeneousArray,
|
||||
NonBooleanCondition,
|
||||
NonCompatibleBranchTypes,
|
||||
NonEqualTypesInExpression,
|
||||
NonExistentSymbol,
|
||||
NoMainFoundInProject,
|
||||
NoCompilerVersionWarning,
|
||||
MultipleMainInComponent,
|
||||
TemplateCallAsArgument,
|
||||
TemplateWrongNumberOfArguments,
|
||||
TemplateWithReturnStatement,
|
||||
TypeCantBeUseAsCondition,
|
||||
EmptyArrayInlineDeclaration,
|
||||
PrefixOperatorWithWrongTypes,
|
||||
InfixOperatorWithWrongTypes,
|
||||
InvalidArgumentInCall,
|
||||
InconsistentReturnTypesInBlock,
|
||||
InconsistentStaticInformation,
|
||||
InvalidArrayAccess,
|
||||
InvalidSignalAccess,
|
||||
InvalidArraySize,
|
||||
InvalidArrayType,
|
||||
ForStatementIllConstructed,
|
||||
BadArrayAccess,
|
||||
AssigningAComponentTwice,
|
||||
AssigningASignalTwice,
|
||||
NotAllowedOperation,
|
||||
ConstraintGeneratorInFunction,
|
||||
WrongSignalTags,
|
||||
InvalidPartialArray,
|
||||
MustBeSingleArithmetic,
|
||||
ExpectedDimDiffGotDim(usize, usize),
|
||||
RuntimeError,
|
||||
UnknownTemplate,
|
||||
NonQuadratic,
|
||||
NonConstantArrayLength,
|
||||
NonComputableExpression,
|
||||
// Constraint analysis codes
|
||||
UnconstrainedSignal,
|
||||
OneConstraintIntermediate,
|
||||
NoOutputInInstance,
|
||||
ErrorWat2Wasm,
|
||||
// Circom Spectacle specific codes
|
||||
ShadowedVariable,
|
||||
}
|
||||
impl fmt::Display for ReportCode {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||
use self::ReportCode::*;
|
||||
let string_format = match self {
|
||||
ParseFail => "P1000",
|
||||
NoMainFoundInProject => "P1001",
|
||||
MultipleMainInComponent => "P1002",
|
||||
CompilerVersionError => "P1003",
|
||||
NoCompilerVersionWarning => "P1004",
|
||||
WrongTypesInAssignOperation => "T2000",
|
||||
UndefinedFunction => "T2001",
|
||||
UndefinedTemplate => "T2002",
|
||||
UninitializedSymbolInExpression => "T2003",
|
||||
UnableToTypeFunction => "T2004",
|
||||
UnreachableConstraints => "T2005",
|
||||
SameFunctionDeclaredTwice => "T2006",
|
||||
SameTemplateDeclaredTwice => "T2007",
|
||||
SameSymbolDeclaredTwice => "T2008",
|
||||
StaticInfoWasOverwritten => "T2009",
|
||||
SignalInLineInitialization => "T2010",
|
||||
SignalOutsideOriginalScope => "T2011",
|
||||
FunctionWrongNumberOfArguments => "T2012",
|
||||
FunctionInconsistentTyping => "T2013",
|
||||
FunctionPathWithoutReturn => "T2014",
|
||||
FunctionReturnError => "T2015",
|
||||
ForbiddenDeclarationInFunction => "T2016",
|
||||
NonHomogeneousArray => "T2017",
|
||||
NonBooleanCondition => "T2018",
|
||||
NonCompatibleBranchTypes => "T2019",
|
||||
NonEqualTypesInExpression => "T2020",
|
||||
NonExistentSymbol => "T2021",
|
||||
TemplateCallAsArgument => "T2022",
|
||||
TemplateWrongNumberOfArguments => "T2023",
|
||||
TemplateWithReturnStatement => "T2024",
|
||||
TypeCantBeUseAsCondition => "T2025",
|
||||
EmptyArrayInlineDeclaration => "T2026",
|
||||
PrefixOperatorWithWrongTypes => "T2027",
|
||||
InfixOperatorWithWrongTypes => "T2028",
|
||||
InvalidArgumentInCall => "T2029",
|
||||
InconsistentReturnTypesInBlock => "T2030",
|
||||
InconsistentStaticInformation => "T2031",
|
||||
InvalidArrayAccess => "T2032",
|
||||
InvalidSignalAccess => "T2046",
|
||||
InvalidArraySize => "T2033",
|
||||
InvalidArrayType => "T2034",
|
||||
ForStatementIllConstructed => "T2035",
|
||||
BadArrayAccess => "T2035",
|
||||
AssigningAComponentTwice => "T2036",
|
||||
AssigningASignalTwice => "T2037",
|
||||
NotAllowedOperation => "T2038",
|
||||
ConstraintGeneratorInFunction => "T2039",
|
||||
WrongSignalTags => "T2040",
|
||||
AssertWrongType => "T2041",
|
||||
UnknownIndex => "T2042",
|
||||
InvalidPartialArray => "T2043",
|
||||
MustBeSingleArithmetic => "T2044",
|
||||
ExpectedDimDiffGotDim(..) => "T2045",
|
||||
RuntimeError => "T3001",
|
||||
UnknownDimension => "T20460",
|
||||
UnknownTemplate => "T20461",
|
||||
NonQuadratic => "T20462",
|
||||
NonConstantArrayLength => "T20463",
|
||||
NonComputableExpression => "T20464",
|
||||
WrongNumberOfArguments(..) => "T20465",
|
||||
// Constraint analysis codes
|
||||
UnconstrainedSignal => "CA01",
|
||||
OneConstraintIntermediate => "CA02",
|
||||
NoOutputInInstance => "CA03",
|
||||
ErrorWat2Wasm => "W01",
|
||||
// Circom Spectacle specific codes
|
||||
ShadowedVariable => "CS0001",
|
||||
};
|
||||
f.write_str(string_format)
|
||||
}
|
||||
}
|
||||
143
program_structure/src/program_library/error_definition.rs
Normal file
143
program_structure/src/program_library/error_definition.rs
Normal file
@@ -0,0 +1,143 @@
|
||||
use super::error_code::ReportCode;
|
||||
use super::file_definition::{FileID, FileLibrary, FileLocation};
|
||||
use codespan_reporting::diagnostic::{Diagnostic, Label};
|
||||
use codespan_reporting::term;
|
||||
|
||||
pub type ReportCollection = Vec<Report>;
|
||||
pub type DiagnosticCode = String;
|
||||
type ReportLabel = Label<FileID>;
|
||||
type ReportNote = String;
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
enum MessageCategory {
|
||||
Error,
|
||||
Warning,
|
||||
Info,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Report {
|
||||
category: MessageCategory,
|
||||
message: String,
|
||||
primary: Vec<ReportLabel>,
|
||||
secondary: Vec<ReportLabel>,
|
||||
notes: Vec<ReportNote>,
|
||||
}
|
||||
impl Report {
|
||||
fn new(category: MessageCategory, message: String, _code: ReportCode) -> Report {
|
||||
Report {
|
||||
category,
|
||||
message,
|
||||
primary: Vec::new(),
|
||||
secondary: Vec::new(),
|
||||
notes: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn print_reports(reports: &[Report], file_library: &FileLibrary) {
|
||||
use codespan_reporting::term::termcolor::{ColorChoice, StandardStream};
|
||||
let writer = StandardStream::stderr(ColorChoice::Always);
|
||||
let mut config = term::Config::default();
|
||||
let mut diagnostics = Vec::new();
|
||||
let files = file_library.to_storage();
|
||||
for report in reports.iter() {
|
||||
diagnostics.push(report.to_diagnostic());
|
||||
}
|
||||
config.styles.header_warning.set_intense(false);
|
||||
for diagnostic in diagnostics.iter() {
|
||||
let print_result = term::emit(&mut writer.lock(), &config, files, &diagnostic);
|
||||
if print_result.is_err() {
|
||||
panic!("Error printing reports")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn error(message: String, code: ReportCode) -> Report {
|
||||
Report::new(MessageCategory::Error, message, code)
|
||||
}
|
||||
|
||||
pub fn warning(message: String, code: ReportCode) -> Report {
|
||||
Report::new(MessageCategory::Warning, message, code)
|
||||
}
|
||||
|
||||
pub fn info(message: String, code: ReportCode) -> Report {
|
||||
Report::new(MessageCategory::Info, message, code)
|
||||
}
|
||||
|
||||
pub fn add_primary(
|
||||
&mut self,
|
||||
location: FileLocation,
|
||||
file_id: FileID,
|
||||
message: String,
|
||||
) -> &mut Self {
|
||||
let label = ReportLabel::primary(file_id, location).with_message(message);
|
||||
self.get_mut_primary().push(label);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn add_secondary(
|
||||
&mut self,
|
||||
location: FileLocation,
|
||||
file_id: FileID,
|
||||
possible_message: Option<String>,
|
||||
) -> &mut Self {
|
||||
let mut label = ReportLabel::secondary(file_id, location);
|
||||
if let Option::Some(message) = possible_message {
|
||||
label = label.with_message(message);
|
||||
}
|
||||
self.get_mut_secondary().push(label);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn add_note(&mut self, note: String) -> &mut Self {
|
||||
self.get_mut_notes().push(note);
|
||||
self
|
||||
}
|
||||
|
||||
fn to_diagnostic(&self) -> Diagnostic<FileID> {
|
||||
let mut labels = self.get_primary().clone();
|
||||
let mut secondary = self.get_secondary().clone();
|
||||
labels.append(&mut secondary);
|
||||
|
||||
match self.get_category() {
|
||||
MessageCategory::Error => Diagnostic::error(),
|
||||
MessageCategory::Warning => Diagnostic::warning(),
|
||||
MessageCategory::Info => Diagnostic::note(),
|
||||
}
|
||||
.with_message(self.get_message())
|
||||
.with_labels(labels)
|
||||
.with_notes(self.get_notes().clone())
|
||||
}
|
||||
|
||||
fn get_category(&self) -> &MessageCategory {
|
||||
&self.category
|
||||
}
|
||||
|
||||
fn get_message(&self) -> &String {
|
||||
&self.message
|
||||
}
|
||||
|
||||
fn get_primary(&self) -> &Vec<ReportLabel> {
|
||||
&self.primary
|
||||
}
|
||||
|
||||
fn get_mut_primary(&mut self) -> &mut Vec<ReportLabel> {
|
||||
&mut self.primary
|
||||
}
|
||||
|
||||
fn get_secondary(&self) -> &Vec<ReportLabel> {
|
||||
&self.secondary
|
||||
}
|
||||
|
||||
fn get_mut_secondary(&mut self) -> &mut Vec<ReportLabel> {
|
||||
&mut self.secondary
|
||||
}
|
||||
|
||||
fn get_notes(&self) -> &Vec<ReportNote> {
|
||||
&self.notes
|
||||
}
|
||||
|
||||
fn get_mut_notes(&mut self) -> &mut Vec<ReportNote> {
|
||||
&mut self.notes
|
||||
}
|
||||
}
|
||||
46
program_structure/src/program_library/file_definition.rs
Normal file
46
program_structure/src/program_library/file_definition.rs
Normal file
@@ -0,0 +1,46 @@
|
||||
use codespan_reporting::files::{Files, SimpleFiles};
|
||||
use std::ops::Range;
|
||||
|
||||
pub type FileSource = String;
|
||||
pub type FilePath = String;
|
||||
pub type FileID = usize;
|
||||
pub type FileLocation = Range<usize>;
|
||||
type FileStorage = SimpleFiles<FilePath, FileSource>;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct FileLibrary {
|
||||
files: FileStorage,
|
||||
}
|
||||
|
||||
impl Default for FileLibrary {
|
||||
fn default() -> Self {
|
||||
FileLibrary { files: FileStorage::new() }
|
||||
}
|
||||
}
|
||||
|
||||
impl FileLibrary {
|
||||
pub fn new() -> FileLibrary {
|
||||
FileLibrary::default()
|
||||
}
|
||||
pub fn add_file(&mut self, file_name: FilePath, file_source: FileSource) -> FileID {
|
||||
self.get_mut_files().add(file_name, file_source)
|
||||
}
|
||||
pub fn get_line(&self, start: usize, file_id: FileID) -> Option<usize> {
|
||||
match self.files.line_index(file_id, start) {
|
||||
Some(lines) => Some(lines + 1),
|
||||
None => None,
|
||||
}
|
||||
}
|
||||
pub fn to_storage(&self) -> &FileStorage {
|
||||
&self.get_files()
|
||||
}
|
||||
fn get_files(&self) -> &FileStorage {
|
||||
&self.files
|
||||
}
|
||||
fn get_mut_files(&mut self) -> &mut FileStorage {
|
||||
&mut self.files
|
||||
}
|
||||
}
|
||||
pub fn generate_file_location(start: usize, end: usize) -> FileLocation {
|
||||
start..end
|
||||
}
|
||||
67
program_structure/src/program_library/function_data.rs
Normal file
67
program_structure/src/program_library/function_data.rs
Normal file
@@ -0,0 +1,67 @@
|
||||
use super::ast::{FillMeta, Statement};
|
||||
use super::file_definition::FileID;
|
||||
use crate::file_definition::FileLocation;
|
||||
use std::collections::HashMap;
|
||||
|
||||
pub type FunctionInfo = HashMap<String, FunctionData>;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct FunctionData {
|
||||
name: String,
|
||||
file_id: FileID,
|
||||
num_of_params: usize,
|
||||
name_of_params: Vec<String>,
|
||||
param_location: FileLocation,
|
||||
body: Statement,
|
||||
}
|
||||
|
||||
impl FunctionData {
|
||||
pub fn new(
|
||||
name: String,
|
||||
file_id: FileID,
|
||||
mut body: Statement,
|
||||
num_of_params: usize,
|
||||
name_of_params: Vec<String>,
|
||||
param_location: FileLocation,
|
||||
elem_id: &mut usize,
|
||||
) -> FunctionData {
|
||||
body.fill(file_id, elem_id);
|
||||
FunctionData { name, file_id, body, name_of_params, param_location, num_of_params }
|
||||
}
|
||||
pub fn get_file_id(&self) -> FileID {
|
||||
self.file_id
|
||||
}
|
||||
pub fn get_body(&self) -> &Statement {
|
||||
&self.body
|
||||
}
|
||||
pub fn get_body_as_vec(&self) -> &Vec<Statement> {
|
||||
match &self.body {
|
||||
Statement::Block { stmts, .. } => stmts,
|
||||
_ => panic!("Function body should be a block"),
|
||||
}
|
||||
}
|
||||
pub fn get_mut_body(&mut self) -> &mut Statement {
|
||||
&mut self.body
|
||||
}
|
||||
pub fn replace_body(&mut self, new: Statement) -> Statement {
|
||||
std::mem::replace(&mut self.body, new)
|
||||
}
|
||||
pub fn get_mut_body_as_vec(&mut self) -> &mut Vec<Statement> {
|
||||
match &mut self.body {
|
||||
Statement::Block { stmts, .. } => stmts,
|
||||
_ => panic!("Function body should be a block"),
|
||||
}
|
||||
}
|
||||
pub fn get_param_location(&self) -> FileLocation {
|
||||
self.param_location.clone()
|
||||
}
|
||||
pub fn get_num_of_params(&self) -> usize {
|
||||
self.num_of_params
|
||||
}
|
||||
pub fn get_name_of_params(&self) -> &Vec<String> {
|
||||
&self.name_of_params
|
||||
}
|
||||
pub fn get_name(&self) -> &str {
|
||||
&self.name
|
||||
}
|
||||
}
|
||||
8
program_structure/src/program_library/mod.rs
Normal file
8
program_structure/src/program_library/mod.rs
Normal file
@@ -0,0 +1,8 @@
|
||||
use super::ast;
|
||||
pub mod error_code;
|
||||
pub mod error_definition;
|
||||
pub mod file_definition;
|
||||
pub mod function_data;
|
||||
pub mod program_archive;
|
||||
pub mod program_merger;
|
||||
pub mod template_data;
|
||||
134
program_structure/src/program_library/program_archive.rs
Normal file
134
program_structure/src/program_library/program_archive.rs
Normal file
@@ -0,0 +1,134 @@
|
||||
use super::ast::{Definition, Expression, MainComponent};
|
||||
use super::file_definition::{FileID, FileLibrary};
|
||||
use super::function_data::{FunctionData, FunctionInfo};
|
||||
use super::program_merger::Merger;
|
||||
use super::template_data::{TemplateData, TemplateInfo};
|
||||
use crate::abstract_syntax_tree::ast::FillMeta;
|
||||
use std::collections::HashSet;
|
||||
use crate::error_definition::Report;
|
||||
|
||||
type Contents = Vec<(FileID, Vec<Definition>)>;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct ProgramArchive {
|
||||
pub id_max: usize,
|
||||
pub file_id_main: FileID,
|
||||
pub file_library: FileLibrary,
|
||||
pub functions: FunctionInfo,
|
||||
pub templates: TemplateInfo,
|
||||
pub function_keys: HashSet<String>,
|
||||
pub template_keys: HashSet<String>,
|
||||
pub public_inputs: Vec<String>,
|
||||
pub initial_template_call: Expression,
|
||||
}
|
||||
impl ProgramArchive {
|
||||
pub fn new(
|
||||
file_library: FileLibrary,
|
||||
file_id_main: FileID,
|
||||
main_component: MainComponent,
|
||||
program_contents: Contents,
|
||||
) -> Result<ProgramArchive, (FileLibrary, Vec<Report>)> {
|
||||
let mut merger = Merger::new();
|
||||
let mut reports = vec![];
|
||||
for (file_id, definitions) in program_contents {
|
||||
if let Err(mut errs) = merger.add_definitions(file_id, definitions) {
|
||||
reports.append(&mut errs);
|
||||
}
|
||||
}
|
||||
let (mut fresh_id, functions, templates) = merger.decompose();
|
||||
let mut function_keys = HashSet::new();
|
||||
let mut template_keys = HashSet::new();
|
||||
for key in functions.keys() {
|
||||
function_keys.insert(key.clone());
|
||||
}
|
||||
for key in templates.keys() {
|
||||
template_keys.insert(key.clone());
|
||||
}
|
||||
let (public_inputs, mut initial_template_call) = main_component;
|
||||
initial_template_call.fill(file_id_main, &mut fresh_id);
|
||||
if reports.is_empty() {
|
||||
Ok(ProgramArchive {
|
||||
id_max: fresh_id,
|
||||
file_id_main,
|
||||
file_library,
|
||||
functions,
|
||||
templates,
|
||||
public_inputs,
|
||||
initial_template_call,
|
||||
function_keys,
|
||||
template_keys,
|
||||
})
|
||||
} else {
|
||||
Err((file_library, reports))
|
||||
}
|
||||
|
||||
}
|
||||
//file_id_main
|
||||
pub fn get_file_id_main(&self) -> &FileID {
|
||||
&self.file_id_main
|
||||
}
|
||||
//template functions
|
||||
pub fn contains_template(&self, template_name: &str) -> bool {
|
||||
self.templates.contains_key(template_name)
|
||||
}
|
||||
pub fn get_template_data(&self, template_name: &str) -> &TemplateData {
|
||||
assert!(self.contains_template(template_name));
|
||||
self.templates.get(template_name).unwrap()
|
||||
}
|
||||
pub fn get_mut_template_data(&mut self, template_name: &str) -> &mut TemplateData {
|
||||
assert!(self.contains_template(template_name));
|
||||
self.templates.get_mut(template_name).unwrap()
|
||||
}
|
||||
pub fn get_template_names(&self) -> &HashSet<String> {
|
||||
&self.template_keys
|
||||
}
|
||||
pub fn get_templates(&self) -> &TemplateInfo {
|
||||
&self.templates
|
||||
}
|
||||
pub fn get_mut_templates(&mut self) -> &mut TemplateInfo {
|
||||
&mut self.templates
|
||||
}
|
||||
|
||||
pub fn remove_template(&mut self, id: &str) {
|
||||
self.template_keys.remove(id);
|
||||
self.templates.remove(id);
|
||||
}
|
||||
|
||||
//functions functions
|
||||
pub fn contains_function(&self, function_name: &str) -> bool {
|
||||
self.get_functions().contains_key(function_name)
|
||||
}
|
||||
pub fn get_function_data(&self, function_name: &str) -> &FunctionData {
|
||||
assert!(self.contains_function(function_name));
|
||||
self.get_functions().get(function_name).unwrap()
|
||||
}
|
||||
pub fn get_mut_function_data(&mut self, function_name: &str) -> &mut FunctionData {
|
||||
assert!(self.contains_function(function_name));
|
||||
self.functions.get_mut(function_name).unwrap()
|
||||
}
|
||||
pub fn get_function_names(&self) -> &HashSet<String> {
|
||||
&self.function_keys
|
||||
}
|
||||
pub fn get_functions(&self) -> &FunctionInfo {
|
||||
&self.functions
|
||||
}
|
||||
pub fn get_mut_functions(&mut self) -> &mut FunctionInfo {
|
||||
&mut self.functions
|
||||
}
|
||||
pub fn remove_function(&mut self, id: &str) {
|
||||
self.function_keys.remove(id);
|
||||
self.functions.remove(id);
|
||||
}
|
||||
|
||||
//main_component functions
|
||||
pub fn get_public_inputs_main_component(&self) -> &Vec<String> {
|
||||
&self.public_inputs
|
||||
}
|
||||
pub fn get_main_expression(&self) -> &Expression {
|
||||
&self.initial_template_call
|
||||
}
|
||||
// FileLibrary functions
|
||||
pub fn get_file_library(&self) -> &FileLibrary {
|
||||
&self.file_library
|
||||
}
|
||||
}
|
||||
107
program_structure/src/program_library/program_merger.rs
Normal file
107
program_structure/src/program_library/program_merger.rs
Normal file
@@ -0,0 +1,107 @@
|
||||
use super::ast::Definition;
|
||||
use super::error_code::ReportCode;
|
||||
use super::error_definition::Report;
|
||||
use super::file_definition::FileID;
|
||||
use super::function_data::{FunctionData, FunctionInfo};
|
||||
use super::template_data::{TemplateData, TemplateInfo};
|
||||
|
||||
pub struct Merger {
|
||||
fresh_id: usize,
|
||||
function_info: FunctionInfo,
|
||||
template_info: TemplateInfo,
|
||||
}
|
||||
impl Default for Merger {
|
||||
fn default() -> Self {
|
||||
Merger {
|
||||
fresh_id: 0,
|
||||
function_info: FunctionInfo::new(),
|
||||
template_info: TemplateInfo::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Merger {
|
||||
pub fn new() -> Merger {
|
||||
Merger::default()
|
||||
}
|
||||
|
||||
pub fn add_definitions(&mut self, file_id: FileID, definitions: Vec<Definition>) -> Result<(), Vec<Report>> {
|
||||
let mut reports = vec![];
|
||||
for definition in definitions {
|
||||
let (name, meta) = match definition {
|
||||
Definition::Template { name, args, arg_location, body, meta, parallel } => {
|
||||
if self.contains_function(&name) || self.contains_template(&name) {
|
||||
(Option::Some(name), meta)
|
||||
} else {
|
||||
let new_data = TemplateData::new(
|
||||
name.clone(),
|
||||
file_id,
|
||||
body,
|
||||
args.len(),
|
||||
args,
|
||||
arg_location,
|
||||
&mut self.fresh_id,
|
||||
parallel,
|
||||
);
|
||||
self.get_mut_template_info().insert(name.clone(), new_data);
|
||||
(Option::None, meta)
|
||||
}
|
||||
}
|
||||
Definition::Function { name, body, args, arg_location, meta } => {
|
||||
if self.contains_function(&name) || self.contains_template(&name) {
|
||||
(Option::Some(name), meta)
|
||||
} else {
|
||||
let new_data = FunctionData::new(
|
||||
name.clone(),
|
||||
file_id,
|
||||
body,
|
||||
args.len(),
|
||||
args,
|
||||
arg_location,
|
||||
&mut self.fresh_id,
|
||||
);
|
||||
self.get_mut_function_info().insert(name.clone(), new_data);
|
||||
(Option::None, meta)
|
||||
}
|
||||
}
|
||||
};
|
||||
if let Option::Some(definition_name) = name {
|
||||
let mut report = Report::error(
|
||||
String::from("Duplicated callable symbol"),
|
||||
ReportCode::SameSymbolDeclaredTwice,
|
||||
);
|
||||
report.add_primary(
|
||||
meta.file_location(),
|
||||
file_id,
|
||||
format!("{} is already in use", definition_name),
|
||||
);
|
||||
reports.push(report);
|
||||
}
|
||||
}
|
||||
if reports.is_empty() { Ok(()) } else { Err(reports) }
|
||||
}
|
||||
pub fn contains_function(&self, function_name: &str) -> bool {
|
||||
self.get_function_info().contains_key(function_name)
|
||||
}
|
||||
fn get_function_info(&self) -> &FunctionInfo {
|
||||
&self.function_info
|
||||
}
|
||||
fn get_mut_function_info(&mut self) -> &mut FunctionInfo {
|
||||
&mut self.function_info
|
||||
}
|
||||
|
||||
pub fn contains_template(&self, template_name: &str) -> bool {
|
||||
self.get_template_info().contains_key(template_name)
|
||||
}
|
||||
fn get_template_info(&self) -> &TemplateInfo {
|
||||
&self.template_info
|
||||
}
|
||||
fn get_mut_template_info(&mut self) -> &mut TemplateInfo {
|
||||
&mut self.template_info
|
||||
}
|
||||
|
||||
|
||||
pub fn decompose(self) -> (usize, FunctionInfo, TemplateInfo) {
|
||||
(self.fresh_id, self.function_info, self.template_info)
|
||||
}
|
||||
}
|
||||
142
program_structure/src/program_library/template_data.rs
Normal file
142
program_structure/src/program_library/template_data.rs
Normal file
@@ -0,0 +1,142 @@
|
||||
use super::ast;
|
||||
use super::ast::{FillMeta, SignalElementType, Statement};
|
||||
use super::file_definition::FileID;
|
||||
use crate::file_definition::FileLocation;
|
||||
use std::collections::hash_map::HashMap;
|
||||
|
||||
pub type TemplateInfo = HashMap<String, TemplateData>;
|
||||
type SignalInfo = HashMap<String, (usize, SignalElementType)>;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct TemplateData {
|
||||
file_id: FileID,
|
||||
name: String,
|
||||
body: Statement,
|
||||
num_of_params: usize,
|
||||
name_of_params: Vec<String>,
|
||||
param_location: FileLocation,
|
||||
input_signals: SignalInfo,
|
||||
output_signals: SignalInfo,
|
||||
is_parallel: bool,
|
||||
}
|
||||
|
||||
impl TemplateData {
|
||||
pub fn new(
|
||||
name: String,
|
||||
file_id: FileID,
|
||||
mut body: Statement,
|
||||
num_of_params: usize,
|
||||
name_of_params: Vec<String>,
|
||||
param_location: FileLocation,
|
||||
elem_id: &mut usize,
|
||||
is_parallel: bool,
|
||||
) -> TemplateData {
|
||||
body.fill(file_id, elem_id);
|
||||
let mut input_signals = SignalInfo::new();
|
||||
let mut output_signals = SignalInfo::new();
|
||||
fill_inputs_and_outputs(&body, &mut input_signals, &mut output_signals);
|
||||
TemplateData {
|
||||
name,
|
||||
file_id,
|
||||
body,
|
||||
num_of_params,
|
||||
name_of_params,
|
||||
param_location,
|
||||
input_signals,
|
||||
output_signals,
|
||||
is_parallel,
|
||||
}
|
||||
}
|
||||
pub fn get_file_id(&self) -> FileID {
|
||||
self.file_id
|
||||
}
|
||||
pub fn get_body(&self) -> &Statement {
|
||||
&self.body
|
||||
}
|
||||
pub fn get_body_as_vec(&self) -> &Vec<Statement> {
|
||||
match &self.body {
|
||||
Statement::Block { stmts, .. } => stmts,
|
||||
_ => panic!("Function body should be a block"),
|
||||
}
|
||||
}
|
||||
pub fn get_mut_body(&mut self) -> &mut Statement {
|
||||
&mut self.body
|
||||
}
|
||||
pub fn get_mut_body_as_vec(&mut self) -> &mut Vec<Statement> {
|
||||
match &mut self.body {
|
||||
Statement::Block { stmts, .. } => stmts,
|
||||
_ => panic!("Function body should be a block"),
|
||||
}
|
||||
}
|
||||
pub fn get_num_of_params(&self) -> usize {
|
||||
self.num_of_params
|
||||
}
|
||||
pub fn get_param_location(&self) -> FileLocation {
|
||||
self.param_location.clone()
|
||||
}
|
||||
pub fn get_name_of_params(&self) -> &Vec<String> {
|
||||
&self.name_of_params
|
||||
}
|
||||
pub fn get_input_info(&self, name: &str) -> Option<&(usize, SignalElementType)> {
|
||||
self.input_signals.get(name)
|
||||
}
|
||||
pub fn get_output_info(&self, name: &str) -> Option<&(usize, SignalElementType)> {
|
||||
self.output_signals.get(name)
|
||||
}
|
||||
pub fn get_inputs(&self) -> &SignalInfo {
|
||||
&self.input_signals
|
||||
}
|
||||
pub fn get_outputs(&self) -> &SignalInfo {
|
||||
&self.output_signals
|
||||
}
|
||||
pub fn get_name(&self) -> &str {
|
||||
&self.name
|
||||
}
|
||||
pub fn is_parallel(&self) -> bool {
|
||||
self.is_parallel
|
||||
}
|
||||
}
|
||||
|
||||
fn fill_inputs_and_outputs(
|
||||
template_statement: &Statement,
|
||||
input_signals: &mut SignalInfo,
|
||||
output_signals: &mut SignalInfo,
|
||||
) {
|
||||
match template_statement {
|
||||
Statement::IfThenElse { if_case, else_case, .. } => {
|
||||
fill_inputs_and_outputs(if_case, input_signals, output_signals);
|
||||
if let Option::Some(else_value) = else_case {
|
||||
fill_inputs_and_outputs(else_value, input_signals, output_signals);
|
||||
}
|
||||
}
|
||||
Statement::Block { stmts, .. } => {
|
||||
for stmt in stmts.iter() {
|
||||
fill_inputs_and_outputs(stmt, input_signals, output_signals);
|
||||
}
|
||||
}
|
||||
Statement::While { stmt, .. } => {
|
||||
fill_inputs_and_outputs(stmt, input_signals, output_signals);
|
||||
}
|
||||
Statement::InitializationBlock { initializations, .. } => {
|
||||
for initialization in initializations.iter() {
|
||||
fill_inputs_and_outputs(initialization, input_signals, output_signals);
|
||||
}
|
||||
}
|
||||
Statement::Declaration { xtype, name, dimensions, .. } => {
|
||||
if let ast::VariableType::Signal(stype, tag) = xtype {
|
||||
let signal_name = name.clone();
|
||||
let dim = dimensions.len();
|
||||
match stype {
|
||||
ast::SignalType::Input => {
|
||||
input_signals.insert(signal_name, (dim, *tag));
|
||||
}
|
||||
ast::SignalType::Output => {
|
||||
output_signals.insert(signal_name, (dim, *tag));
|
||||
}
|
||||
_ => {} //no need to deal with intermediate signals
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
27
program_structure/src/utils/constants.rs
Normal file
27
program_structure/src/utils/constants.rs
Normal file
@@ -0,0 +1,27 @@
|
||||
use num_bigint::BigInt;
|
||||
|
||||
const P_STR: &str = "21888242871839275222246405745257275088548364400416034343698204186575808495617";
|
||||
|
||||
pub struct UsefulConstants {
|
||||
p: BigInt,
|
||||
}
|
||||
|
||||
impl Clone for UsefulConstants {
|
||||
fn clone(&self) -> Self {
|
||||
UsefulConstants { p: self.p.clone() }
|
||||
}
|
||||
}
|
||||
impl Default for UsefulConstants {
|
||||
fn default() -> Self {
|
||||
UsefulConstants { p: BigInt::parse_bytes(P_STR.as_bytes(), 10).expect("can not parse p") }
|
||||
}
|
||||
}
|
||||
|
||||
impl UsefulConstants {
|
||||
pub fn new() -> UsefulConstants {
|
||||
UsefulConstants::default()
|
||||
}
|
||||
pub fn get_p(&self) -> &BigInt {
|
||||
&self.p
|
||||
}
|
||||
}
|
||||
468
program_structure/src/utils/environment.rs
Normal file
468
program_structure/src/utils/environment.rs
Normal file
@@ -0,0 +1,468 @@
|
||||
use std::collections::HashMap;
|
||||
use std::hash::Hash;
|
||||
use std::marker::PhantomData;
|
||||
|
||||
pub trait VarInfo {}
|
||||
pub trait SignalInfo {}
|
||||
pub trait ComponentInfo {}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct OnlyVars;
|
||||
impl VarInfo for OnlyVars {}
|
||||
#[derive(Clone)]
|
||||
pub struct OnlySignals;
|
||||
impl SignalInfo for OnlySignals {}
|
||||
#[derive(Clone)]
|
||||
pub struct OnlyComponents;
|
||||
impl ComponentInfo for OnlyComponents {}
|
||||
#[derive(Clone)]
|
||||
pub struct FullEnvironment;
|
||||
impl VarInfo for FullEnvironment {}
|
||||
impl SignalInfo for FullEnvironment {}
|
||||
impl ComponentInfo for FullEnvironment {}
|
||||
|
||||
pub type VarEnvironment<VC> = RawEnvironment<OnlyVars, (), (), VC>;
|
||||
pub type SignalEnvironment<SC> = RawEnvironment<OnlySignals, (), SC, ()>;
|
||||
pub type ComponentEnvironment<CC> = RawEnvironment<OnlyComponents, CC, (), ()>;
|
||||
pub type CircomEnvironment<CC, SC, VC> = RawEnvironment<FullEnvironment, CC, SC, VC>;
|
||||
|
||||
pub enum CircomEnvironmentError {
|
||||
NonExistentSymbol,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct RawEnvironment<T, CC, SC, VC> {
|
||||
components: HashMap<String, CC>,
|
||||
inputs: HashMap<String, SC>,
|
||||
outputs: HashMap<String, SC>,
|
||||
intermediates: HashMap<String, SC>,
|
||||
variables: Vec<VariableBlock<VC>>,
|
||||
behaviour: PhantomData<T>,
|
||||
}
|
||||
impl<T, CC, SC, VC> Default for RawEnvironment<T, CC, SC, VC> {
|
||||
fn default() -> Self {
|
||||
let variables = vec![VariableBlock::new()];
|
||||
RawEnvironment {
|
||||
components: HashMap::new(),
|
||||
inputs: HashMap::new(),
|
||||
outputs: HashMap::new(),
|
||||
intermediates: HashMap::new(),
|
||||
variables,
|
||||
behaviour: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
impl<T, CC, SC, VC> RawEnvironment<T, CC, SC, VC>
|
||||
where
|
||||
T: VarInfo + SignalInfo + ComponentInfo,
|
||||
{
|
||||
pub fn has_symbol(&self, symbol: &str) -> bool {
|
||||
self.has_signal(symbol) || self.has_component(symbol) || self.has_variable(symbol)
|
||||
}
|
||||
}
|
||||
impl<T, CC, SC, VC> RawEnvironment<T, CC, SC, VC> {
|
||||
pub fn merge(
|
||||
left: RawEnvironment<T, CC, SC, VC>,
|
||||
right: RawEnvironment<T, CC, SC, VC>,
|
||||
using: fn(VC, VC) -> VC,
|
||||
) -> RawEnvironment<T, CC, SC, VC> {
|
||||
let mut components = left.components;
|
||||
let mut inputs = left.inputs;
|
||||
let mut outputs = left.outputs;
|
||||
let mut intermediates = left.intermediates;
|
||||
components.extend(right.components);
|
||||
inputs.extend(right.inputs);
|
||||
outputs.extend(right.outputs);
|
||||
intermediates.extend(right.intermediates);
|
||||
let mut variables_left = left.variables;
|
||||
let mut variables_right = right.variables;
|
||||
let mut variables = Vec::new();
|
||||
while !variables_left.is_empty() && !variables_right.is_empty() {
|
||||
let left_block = variables_left.pop().unwrap();
|
||||
let right_block = variables_right.pop().unwrap();
|
||||
let merged_blocks = VariableBlock::merge(left_block, right_block, using);
|
||||
variables.push(merged_blocks);
|
||||
}
|
||||
variables.reverse();
|
||||
RawEnvironment {
|
||||
components,
|
||||
inputs,
|
||||
intermediates,
|
||||
outputs,
|
||||
variables,
|
||||
behaviour: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
impl<T, CC, SC, VC> RawEnvironment<T, CC, SC, VC>
|
||||
where
|
||||
T: VarInfo,
|
||||
{
|
||||
fn block_with_variable_symbol(&self, symbol: &str) -> Option<&VariableBlock<VC>> {
|
||||
let variables = &self.variables;
|
||||
let mut act = variables.len();
|
||||
while act > 0 {
|
||||
if VariableBlock::contains_variable(&variables[act - 1], symbol) {
|
||||
return Option::Some(&variables[act - 1]);
|
||||
}
|
||||
act -= 1;
|
||||
}
|
||||
Option::None
|
||||
}
|
||||
fn mut_block_with_variable_symbol(&mut self, symbol: &str) -> Option<&mut VariableBlock<VC>> {
|
||||
let variables = &mut self.variables;
|
||||
let mut act = variables.len();
|
||||
while act > 0 {
|
||||
if VariableBlock::contains_variable(&variables[act - 1], symbol) {
|
||||
return Option::Some(&mut variables[act - 1]);
|
||||
}
|
||||
act -= 1;
|
||||
}
|
||||
Option::None
|
||||
}
|
||||
pub fn new() -> RawEnvironment<T, CC, SC, VC> {
|
||||
RawEnvironment::default()
|
||||
}
|
||||
pub fn add_variable_block(&mut self) {
|
||||
self.variables.push(VariableBlock::new());
|
||||
}
|
||||
pub fn remove_variable_block(&mut self) {
|
||||
assert!(!self.variables.is_empty());
|
||||
self.variables.pop();
|
||||
}
|
||||
pub fn add_variable(&mut self, variable_name: &str, content: VC) {
|
||||
assert!(!self.variables.is_empty());
|
||||
let last_block = self.variables.last_mut().unwrap();
|
||||
last_block.add_variable(variable_name, content);
|
||||
}
|
||||
pub fn has_variable(&self, symbol: &str) -> bool {
|
||||
self.block_with_variable_symbol(symbol).is_some()
|
||||
}
|
||||
|
||||
pub fn get_variable(&self, symbol: &str) -> Option<&VC> {
|
||||
let possible_block = self.block_with_variable_symbol(symbol);
|
||||
if let Option::Some(block) = possible_block {
|
||||
Option::Some(block.get_variable(symbol))
|
||||
} else {
|
||||
Option::None
|
||||
}
|
||||
}
|
||||
pub fn get_mut_variable(&mut self, symbol: &str) -> Option<&mut VC> {
|
||||
let possible_block = self.mut_block_with_variable_symbol(symbol);
|
||||
if let Option::Some(block) = possible_block {
|
||||
Option::Some(block.get_mut_variable(symbol))
|
||||
} else {
|
||||
Option::None
|
||||
}
|
||||
}
|
||||
pub fn get_variable_res(&self, symbol: &str) -> Result<&VC, CircomEnvironmentError> {
|
||||
let possible_block = self.block_with_variable_symbol(symbol);
|
||||
if let Option::Some(block) = possible_block {
|
||||
Result::Ok(block.get_variable(symbol))
|
||||
} else {
|
||||
Result::Err(CircomEnvironmentError::NonExistentSymbol)
|
||||
}
|
||||
}
|
||||
pub fn remove_variable(&mut self, symbol: &str) {
|
||||
let possible_block = self.mut_block_with_variable_symbol(symbol);
|
||||
if let Option::Some(block) = possible_block {
|
||||
block.remove_variable(symbol)
|
||||
}
|
||||
}
|
||||
pub fn get_variable_or_break(&self, symbol: &str, file: &str, line: u32) -> &VC {
|
||||
assert!(self.has_variable(symbol), "Method call in file {} line {}", file, line);
|
||||
if let Result::Ok(v) = self.get_variable_res(symbol) {
|
||||
v
|
||||
} else {
|
||||
unreachable!();
|
||||
}
|
||||
}
|
||||
pub fn get_mut_variable_mut(
|
||||
&mut self,
|
||||
symbol: &str,
|
||||
) -> Result<&mut VC, CircomEnvironmentError> {
|
||||
let possible_block = self.mut_block_with_variable_symbol(symbol);
|
||||
if let Option::Some(block) = possible_block {
|
||||
Result::Ok(block.get_mut_variable(symbol))
|
||||
} else {
|
||||
Result::Err(CircomEnvironmentError::NonExistentSymbol)
|
||||
}
|
||||
}
|
||||
pub fn get_mut_variable_or_break(&mut self, symbol: &str, file: &str, line: u32) -> &mut VC {
|
||||
assert!(self.has_variable(symbol), "Method call in file {} line {}", file, line);
|
||||
if let Result::Ok(v) = self.get_mut_variable_mut(symbol) {
|
||||
v
|
||||
} else {
|
||||
unreachable!();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, CC, SC, VC> RawEnvironment<T, CC, SC, VC>
|
||||
where
|
||||
T: ComponentInfo,
|
||||
{
|
||||
pub fn add_component(&mut self, component_name: &str, content: CC) {
|
||||
self.components.insert(component_name.to_string(), content);
|
||||
}
|
||||
pub fn remove_component(&mut self, component_name: &str) {
|
||||
self.components.remove(component_name);
|
||||
}
|
||||
pub fn has_component(&self, symbol: &str) -> bool {
|
||||
self.components.contains_key(symbol)
|
||||
}
|
||||
pub fn get_component(&self, symbol: &str) -> Option<&CC> {
|
||||
self.components.get(symbol)
|
||||
}
|
||||
pub fn get_mut_component(&mut self, symbol: &str) -> Option<&mut CC> {
|
||||
self.components.get_mut(symbol)
|
||||
}
|
||||
pub fn get_component_res(&self, symbol: &str) -> Result<&CC, CircomEnvironmentError> {
|
||||
self.components.get(symbol).ok_or_else(|| CircomEnvironmentError::NonExistentSymbol)
|
||||
}
|
||||
pub fn get_component_or_break(&self, symbol: &str, file: &str, line: u32) -> &CC {
|
||||
assert!(self.has_component(symbol), "Method call in file {} line {}", file, line);
|
||||
self.components.get(symbol).unwrap()
|
||||
}
|
||||
pub fn get_mut_component_res(
|
||||
&mut self,
|
||||
symbol: &str,
|
||||
) -> Result<&mut CC, CircomEnvironmentError> {
|
||||
self.components.get_mut(symbol).ok_or_else(|| CircomEnvironmentError::NonExistentSymbol)
|
||||
}
|
||||
pub fn get_mut_component_or_break(&mut self, symbol: &str, file: &str, line: u32) -> &mut CC {
|
||||
assert!(self.has_component(symbol), "Method call in file {} line {}", file, line);
|
||||
self.components.get_mut(symbol).unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, CC, SC, VC> RawEnvironment<T, CC, SC, VC>
|
||||
where
|
||||
T: SignalInfo,
|
||||
{
|
||||
pub fn add_input(&mut self, input_name: &str, content: SC) {
|
||||
self.inputs.insert(input_name.to_string(), content);
|
||||
}
|
||||
pub fn remove_input(&mut self, input_name: &str) {
|
||||
self.inputs.remove(input_name);
|
||||
}
|
||||
pub fn add_output(&mut self, output_name: &str, content: SC) {
|
||||
self.outputs.insert(output_name.to_string(), content);
|
||||
}
|
||||
pub fn remove_output(&mut self, output_name: &str) {
|
||||
self.outputs.remove(output_name);
|
||||
}
|
||||
pub fn add_intermediate(&mut self, intermediate_name: &str, content: SC) {
|
||||
self.intermediates.insert(intermediate_name.to_string(), content);
|
||||
}
|
||||
pub fn remove_intermediate(&mut self, intermediate_name: &str) {
|
||||
self.intermediates.remove(intermediate_name);
|
||||
}
|
||||
pub fn has_input(&self, symbol: &str) -> bool {
|
||||
self.inputs.contains_key(symbol)
|
||||
}
|
||||
pub fn has_output(&self, symbol: &str) -> bool {
|
||||
self.outputs.contains_key(symbol)
|
||||
}
|
||||
pub fn has_intermediate(&self, symbol: &str) -> bool {
|
||||
self.intermediates.contains_key(symbol)
|
||||
}
|
||||
pub fn has_signal(&self, symbol: &str) -> bool {
|
||||
self.has_input(symbol) || self.has_output(symbol) || self.has_intermediate(symbol)
|
||||
}
|
||||
pub fn get_input(&self, symbol: &str) -> Option<&SC> {
|
||||
self.inputs.get(symbol)
|
||||
}
|
||||
pub fn get_mut_input(&mut self, symbol: &str) -> Option<&mut SC> {
|
||||
self.inputs.get_mut(symbol)
|
||||
}
|
||||
pub fn get_input_res(&self, symbol: &str) -> Result<&SC, CircomEnvironmentError> {
|
||||
self.inputs.get(symbol).ok_or_else(|| CircomEnvironmentError::NonExistentSymbol)
|
||||
}
|
||||
pub fn get_input_or_break(&self, symbol: &str, file: &str, line: u32) -> &SC {
|
||||
assert!(self.has_input(symbol), "Method call in file {} line {}", file, line);
|
||||
self.inputs.get(symbol).unwrap()
|
||||
}
|
||||
pub fn get_mut_input_res(&mut self, symbol: &str) -> Result<&mut SC, CircomEnvironmentError> {
|
||||
self.inputs.get_mut(symbol).ok_or_else(|| CircomEnvironmentError::NonExistentSymbol)
|
||||
}
|
||||
pub fn get_mut_input_or_break(&mut self, symbol: &str, file: &str, line: u32) -> &mut SC {
|
||||
assert!(self.has_input(symbol), "Method call in file {} line {}", file, line);
|
||||
self.inputs.get_mut(symbol).unwrap()
|
||||
}
|
||||
|
||||
pub fn get_output(&self, symbol: &str) -> Option<&SC> {
|
||||
self.outputs.get(symbol)
|
||||
}
|
||||
pub fn get_mut_output(&mut self, symbol: &str) -> Option<&mut SC> {
|
||||
self.outputs.get_mut(symbol)
|
||||
}
|
||||
pub fn get_output_res(&self, symbol: &str) -> Result<&SC, CircomEnvironmentError> {
|
||||
self.outputs.get(symbol).ok_or_else(|| CircomEnvironmentError::NonExistentSymbol)
|
||||
}
|
||||
pub fn get_output_or_break(&self, symbol: &str, file: &str, line: u32) -> &SC {
|
||||
assert!(self.has_output(symbol), "Method call in file {} line {}", file, line);
|
||||
self.outputs.get(symbol).unwrap()
|
||||
}
|
||||
pub fn get_mut_output_res(&mut self, symbol: &str) -> Result<&mut SC, CircomEnvironmentError> {
|
||||
self.outputs.get_mut(symbol).ok_or_else(|| CircomEnvironmentError::NonExistentSymbol)
|
||||
}
|
||||
pub fn get_mut_output_or_break(&mut self, symbol: &str, file: &str, line: u32) -> &mut SC {
|
||||
assert!(self.has_output(symbol), "Method call in file {} line {}", file, line);
|
||||
self.outputs.get_mut(symbol).unwrap()
|
||||
}
|
||||
|
||||
pub fn get_intermediate(&self, symbol: &str) -> Option<&SC> {
|
||||
self.intermediates.get(symbol)
|
||||
}
|
||||
pub fn get_mut_intermediate(&mut self, symbol: &str) -> Option<&mut SC> {
|
||||
self.intermediates.get_mut(symbol)
|
||||
}
|
||||
pub fn get_intermediate_res(&self, symbol: &str) -> Result<&SC, CircomEnvironmentError> {
|
||||
self.intermediates.get(symbol).ok_or_else(|| CircomEnvironmentError::NonExistentSymbol)
|
||||
}
|
||||
pub fn get_intermediate_or_break(&self, symbol: &str, file: &str, line: u32) -> &SC {
|
||||
assert!(self.has_intermediate(symbol), "Method call in file {} line {}", file, line);
|
||||
self.intermediates.get(symbol).unwrap()
|
||||
}
|
||||
pub fn get_mut_intermediate_res(
|
||||
&mut self,
|
||||
symbol: &str,
|
||||
) -> Result<&mut SC, CircomEnvironmentError> {
|
||||
self.intermediates.get_mut(symbol).ok_or_else(|| CircomEnvironmentError::NonExistentSymbol)
|
||||
}
|
||||
pub fn get_mut_intermediate_or_break(
|
||||
&mut self,
|
||||
symbol: &str,
|
||||
file: &str,
|
||||
line: u32,
|
||||
) -> &mut SC {
|
||||
assert!(self.has_intermediate(symbol), "Method call in file {} line {}", file, line);
|
||||
self.intermediates.get_mut(symbol).unwrap()
|
||||
}
|
||||
|
||||
pub fn get_signal(&self, symbol: &str) -> Option<&SC> {
|
||||
if self.has_input(symbol) {
|
||||
self.get_input(symbol)
|
||||
} else if self.has_output(symbol) {
|
||||
self.get_output(symbol)
|
||||
} else if self.has_intermediate(symbol) {
|
||||
self.get_intermediate(symbol)
|
||||
} else {
|
||||
Option::None
|
||||
}
|
||||
}
|
||||
pub fn get_mut_signal(&mut self, symbol: &str) -> Option<&mut SC> {
|
||||
if self.has_input(symbol) {
|
||||
self.get_mut_input(symbol)
|
||||
} else if self.has_output(symbol) {
|
||||
self.get_mut_output(symbol)
|
||||
} else if self.has_intermediate(symbol) {
|
||||
self.get_mut_intermediate(symbol)
|
||||
} else {
|
||||
Option::None
|
||||
}
|
||||
}
|
||||
pub fn get_signal_res(&self, symbol: &str) -> Result<&SC, CircomEnvironmentError> {
|
||||
if self.has_input(symbol) {
|
||||
self.get_input_res(symbol)
|
||||
} else if self.has_output(symbol) {
|
||||
self.get_output_res(symbol)
|
||||
} else if self.has_intermediate(symbol) {
|
||||
self.get_intermediate_res(symbol)
|
||||
} else {
|
||||
Result::Err(CircomEnvironmentError::NonExistentSymbol)
|
||||
}
|
||||
}
|
||||
pub fn get_signal_or_break(&self, symbol: &str, file: &str, line: u32) -> &SC {
|
||||
assert!(self.has_signal(symbol), "Method call in file {} line {}", file, line);
|
||||
if let Result::Ok(v) = self.get_signal_res(symbol) {
|
||||
v
|
||||
} else {
|
||||
unreachable!();
|
||||
}
|
||||
}
|
||||
pub fn get_mut_signal_res(&mut self, symbol: &str) -> Result<&mut SC, CircomEnvironmentError> {
|
||||
if self.has_input(symbol) {
|
||||
self.get_mut_input_res(symbol)
|
||||
} else if self.has_output(symbol) {
|
||||
self.get_mut_output_res(symbol)
|
||||
} else if self.has_intermediate(symbol) {
|
||||
self.get_mut_intermediate_res(symbol)
|
||||
} else {
|
||||
Result::Err(CircomEnvironmentError::NonExistentSymbol)
|
||||
}
|
||||
}
|
||||
pub fn get_mut_signal_or_break(&mut self, symbol: &str, file: &str, line: u32) -> &mut SC {
|
||||
assert!(self.has_signal(symbol), "Method call in file {} line {}", file, line);
|
||||
if let Result::Ok(v) = self.get_mut_signal_res(symbol) {
|
||||
v
|
||||
} else {
|
||||
unreachable!();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
struct VariableBlock<VC> {
|
||||
variables: HashMap<String, VC>,
|
||||
}
|
||||
impl<VC> Default for VariableBlock<VC> {
|
||||
fn default() -> Self {
|
||||
VariableBlock { variables: HashMap::new() }
|
||||
}
|
||||
}
|
||||
impl<VC> VariableBlock<VC> {
|
||||
pub fn new() -> VariableBlock<VC> {
|
||||
VariableBlock::default()
|
||||
}
|
||||
pub fn add_variable(&mut self, symbol: &str, content: VC) {
|
||||
self.variables.insert(symbol.to_string(), content);
|
||||
}
|
||||
pub fn remove_variable(&mut self, symbol: &str) {
|
||||
self.variables.remove(symbol);
|
||||
}
|
||||
pub fn contains_variable(&self, symbol: &str) -> bool {
|
||||
self.variables.contains_key(symbol)
|
||||
}
|
||||
pub fn get_variable(&self, symbol: &str) -> &VC {
|
||||
assert!(self.contains_variable(symbol));
|
||||
self.variables.get(symbol).unwrap()
|
||||
}
|
||||
pub fn get_mut_variable(&mut self, symbol: &str) -> &mut VC {
|
||||
assert!(self.contains_variable(symbol));
|
||||
self.variables.get_mut(symbol).unwrap()
|
||||
}
|
||||
pub fn merge(
|
||||
left: VariableBlock<VC>,
|
||||
right: VariableBlock<VC>,
|
||||
using: fn(VC, VC) -> VC,
|
||||
) -> VariableBlock<VC> {
|
||||
let left_block = left.variables;
|
||||
let right_block = right.variables;
|
||||
let result_block = hashmap_union(left_block, right_block, using);
|
||||
VariableBlock { variables: result_block }
|
||||
}
|
||||
}
|
||||
|
||||
fn hashmap_union<K, V>(
|
||||
l: HashMap<K, V>,
|
||||
mut r: HashMap<K, V>,
|
||||
merge_function: fn(V, V) -> V,
|
||||
) -> HashMap<K, V>
|
||||
where
|
||||
K: Hash + Eq,
|
||||
{
|
||||
let mut result = HashMap::new();
|
||||
for (k, v) in l {
|
||||
if let Option::Some(r_v) = r.remove(&k) {
|
||||
result.insert(k, merge_function(v, r_v));
|
||||
} else {
|
||||
result.insert(k, v);
|
||||
}
|
||||
}
|
||||
for (k, v) in r {
|
||||
result.entry(k).or_insert(v);
|
||||
}
|
||||
result
|
||||
}
|
||||
251
program_structure/src/utils/memory_slice.rs
Normal file
251
program_structure/src/utils/memory_slice.rs
Normal file
@@ -0,0 +1,251 @@
|
||||
use num_bigint_dig::BigInt;
|
||||
use std::fmt::{Display, Formatter};
|
||||
pub enum MemoryError {
|
||||
OutOfBoundsError,
|
||||
AssignmentError,
|
||||
InvalidAccess,
|
||||
UnknownSizeDimension,
|
||||
}
|
||||
pub type SliceCapacity = usize;
|
||||
pub type SimpleSlice = MemorySlice<BigInt>;
|
||||
/*
|
||||
Represents the value stored in a element of a circom program.
|
||||
The attribute route stores the dimensions of the slice, used to navigate through them.
|
||||
The length of values is equal to multiplying all the values in route.
|
||||
*/
|
||||
#[derive(Eq, PartialEq)]
|
||||
pub struct MemorySlice<C> {
|
||||
route: Vec<SliceCapacity>,
|
||||
values: Vec<C>,
|
||||
}
|
||||
|
||||
impl<C: Clone> Clone for MemorySlice<C> {
|
||||
fn clone(&self) -> Self {
|
||||
MemorySlice { route: self.route.clone(), values: self.values.clone() }
|
||||
}
|
||||
}
|
||||
|
||||
impl<C: Default + Clone + Display + Eq> Display for MemorySlice<C> {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
if self.values.is_empty() {
|
||||
f.write_str("[]")
|
||||
} else if self.values.len() == 1 {
|
||||
f.write_str(&format!("{}", self.values[0]))
|
||||
} else {
|
||||
let mut msg = format!("[{}", self.values[0]);
|
||||
for i in 1..self.values.len() {
|
||||
msg.push_str(&format!(",{}", self.values[i]));
|
||||
}
|
||||
msg.push_str("]");
|
||||
f.write_str(&msg)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<C: Clone> MemorySlice<C> {
|
||||
// Raw manipulations of the slice
|
||||
fn get_initial_cell(
|
||||
memory_slice: &MemorySlice<C>,
|
||||
access: &[SliceCapacity],
|
||||
) -> Result<SliceCapacity, MemoryError> {
|
||||
if access.len() > memory_slice.route.len() {
|
||||
return Result::Err(MemoryError::OutOfBoundsError);
|
||||
}
|
||||
|
||||
let mut cell = 0;
|
||||
let mut cell_jump = memory_slice.values.len();
|
||||
let mut i: SliceCapacity = 0;
|
||||
while i < access.len() {
|
||||
if access[i] >= memory_slice.route[i] {
|
||||
return Result::Err(MemoryError::OutOfBoundsError);
|
||||
}
|
||||
cell_jump /= memory_slice.route[i];
|
||||
cell += cell_jump * access[i];
|
||||
i += 1;
|
||||
}
|
||||
Result::Ok(cell)
|
||||
}
|
||||
// Returns the new route and the total number of cells
|
||||
// that a slice with such route will have
|
||||
fn generate_new_route_from_access(
|
||||
memory_slice: &MemorySlice<C>,
|
||||
access: &[SliceCapacity],
|
||||
) -> Result<(Vec<SliceCapacity>, SliceCapacity), MemoryError> {
|
||||
if access.len() > memory_slice.route.len() {
|
||||
return Result::Err(MemoryError::OutOfBoundsError);
|
||||
}
|
||||
|
||||
let mut size = Vec::new();
|
||||
let mut number_of_cells = 1;
|
||||
for i in access.len()..memory_slice.route.len() {
|
||||
number_of_cells *= memory_slice.route[i];
|
||||
size.push(memory_slice.route[i]);
|
||||
}
|
||||
Result::Ok((size, number_of_cells))
|
||||
}
|
||||
|
||||
fn generate_slice_from_access(
|
||||
memory_slice: &MemorySlice<C>,
|
||||
access: &[SliceCapacity],
|
||||
) -> Result<MemorySlice<C>, MemoryError> {
|
||||
if access.is_empty() {
|
||||
return Result::Ok(memory_slice.clone());
|
||||
}
|
||||
|
||||
let (size, number_of_cells) =
|
||||
MemorySlice::generate_new_route_from_access(memory_slice, access)?;
|
||||
let mut values = Vec::with_capacity(number_of_cells);
|
||||
let initial_cell = MemorySlice::get_initial_cell(memory_slice, access)?;
|
||||
let mut offset = 0;
|
||||
while offset < number_of_cells {
|
||||
let new_value = memory_slice.values[initial_cell + offset].clone();
|
||||
values.push(new_value);
|
||||
offset += 1;
|
||||
}
|
||||
|
||||
Result::Ok(MemorySlice { route: size, values })
|
||||
}
|
||||
|
||||
// User operations
|
||||
pub fn new(initial_value: &C) -> MemorySlice<C> {
|
||||
MemorySlice::new_with_route(&[], initial_value)
|
||||
}
|
||||
pub fn new_array(route: Vec<SliceCapacity>, values: Vec<C>) -> MemorySlice<C> {
|
||||
MemorySlice { route, values }
|
||||
}
|
||||
pub fn new_with_route(route: &[SliceCapacity], initial_value: &C) -> MemorySlice<C> {
|
||||
let mut length = 1;
|
||||
for i in route {
|
||||
length *= *i;
|
||||
}
|
||||
|
||||
let mut values = Vec::with_capacity(length);
|
||||
for _i in 0..length {
|
||||
values.push(initial_value.clone());
|
||||
}
|
||||
|
||||
MemorySlice { route: route.to_vec(), values }
|
||||
}
|
||||
pub fn insert_values(
|
||||
memory_slice: &mut MemorySlice<C>,
|
||||
access: &[SliceCapacity],
|
||||
new_values: &MemorySlice<C>,
|
||||
) -> Result<(), MemoryError> {
|
||||
let mut cell = MemorySlice::get_initial_cell(memory_slice, access)?;
|
||||
if MemorySlice::get_number_of_cells(new_values)
|
||||
> (MemorySlice::get_number_of_cells(memory_slice) - cell)
|
||||
{
|
||||
return Result::Err(MemoryError::OutOfBoundsError);
|
||||
}
|
||||
for value in new_values.values.iter() {
|
||||
memory_slice.values[cell] = value.clone();
|
||||
cell += 1;
|
||||
}
|
||||
Result::Ok(())
|
||||
}
|
||||
|
||||
pub fn access_values(
|
||||
memory_slice: &MemorySlice<C>,
|
||||
access: &[SliceCapacity],
|
||||
) -> Result<MemorySlice<C>, MemoryError> {
|
||||
MemorySlice::generate_slice_from_access(memory_slice, access)
|
||||
}
|
||||
pub fn get_reference_to_single_value<'a>(
|
||||
memory_slice: &'a MemorySlice<C>,
|
||||
access: &[SliceCapacity],
|
||||
) -> Result<&'a C, MemoryError> {
|
||||
assert_eq!(memory_slice.route.len(), access.len());
|
||||
let cell = MemorySlice::get_initial_cell(memory_slice, access)?;
|
||||
Result::Ok(&memory_slice.values[cell])
|
||||
}
|
||||
pub fn get_mut_reference_to_single_value<'a>(
|
||||
memory_slice: &'a mut MemorySlice<C>,
|
||||
access: &[SliceCapacity],
|
||||
) -> Result<&'a mut C, MemoryError> {
|
||||
assert_eq!(memory_slice.route.len(), access.len());
|
||||
let cell = MemorySlice::get_initial_cell(memory_slice, access)?;
|
||||
Result::Ok(&mut memory_slice.values[cell])
|
||||
}
|
||||
pub fn get_number_of_cells(memory_slice: &MemorySlice<C>) -> SliceCapacity {
|
||||
memory_slice.values.len()
|
||||
}
|
||||
pub fn route(&self) -> &[SliceCapacity] {
|
||||
&self.route
|
||||
}
|
||||
pub fn is_single(&self) -> bool {
|
||||
self.route.is_empty()
|
||||
}
|
||||
|
||||
/*
|
||||
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||
! Calling this function with a MemorySlice !
|
||||
! that has more than one cell will cause !
|
||||
! the compiler to panic. Use carefully !
|
||||
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||
*/
|
||||
pub fn unwrap_to_single(memory_slice: MemorySlice<C>) -> C {
|
||||
assert!(memory_slice.is_single());
|
||||
let mut memory_slice = memory_slice;
|
||||
memory_slice.values.pop().unwrap()
|
||||
}
|
||||
pub fn destruct(self) -> (Vec<SliceCapacity>, Vec<C>) {
|
||||
(self.route, self.values)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
type U32Slice = MemorySlice<u32>;
|
||||
|
||||
#[test]
|
||||
fn memory_slice_vector_initialization() {
|
||||
let route = vec![3, 4];
|
||||
let slice = U32Slice::new_with_route(&route, &0);
|
||||
assert_eq!(U32Slice::get_number_of_cells(&slice), 12);
|
||||
for (dim_0, dim_1) in slice.route.iter().zip(&route) {
|
||||
assert_eq!(*dim_0, *dim_1);
|
||||
}
|
||||
for f in 0..3 {
|
||||
for c in 0..4 {
|
||||
let result = U32Slice::get_reference_to_single_value(&slice, &[f, c]);
|
||||
if let Result::Ok(v) = result {
|
||||
assert_eq!(*v, 0);
|
||||
} else {
|
||||
assert!(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#[test]
|
||||
fn memory_slice_single_initialization() {
|
||||
let slice = U32Slice::new(&4);
|
||||
assert_eq!(U32Slice::get_number_of_cells(&slice), 1);
|
||||
let memory_response = U32Slice::get_reference_to_single_value(&slice, &[]);
|
||||
if let Result::Ok(val) = memory_response {
|
||||
assert_eq!(*val, 4);
|
||||
} else {
|
||||
assert!(false);
|
||||
}
|
||||
}
|
||||
#[test]
|
||||
fn memory_slice_multiple_insertion() {
|
||||
let route = vec![3, 4];
|
||||
let mut slice = U32Slice::new_with_route(&route, &0);
|
||||
let new_row = U32Slice::new_with_route(&[4], &4);
|
||||
|
||||
let res = U32Slice::insert_values(&mut slice, &[2], &new_row);
|
||||
if let Result::Ok(_) = res {
|
||||
for c in 0..4 {
|
||||
let memory_result = U32Slice::get_reference_to_single_value(&slice, &[2, c]);
|
||||
if let Result::Ok(val) = memory_result {
|
||||
assert_eq!(*val, 4);
|
||||
} else {
|
||||
assert!(false);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
assert!(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
3
program_structure/src/utils/mod.rs
Normal file
3
program_structure/src/utils/mod.rs
Normal file
@@ -0,0 +1,3 @@
|
||||
pub mod constants;
|
||||
pub mod environment;
|
||||
pub mod memory_slice;
|
||||
Reference in New Issue
Block a user