Skip to content

Commit

Permalink
Generate a JSON abi from a .sw file (FuelLabs#343)
Browse files Browse the repository at this point in the history
* first commit

* Pass around a JsonABI object.

* Creating the JSON abi output.

* Complete abi json functionality.

* format

* Undo the mistakes of past. i.e. type inference means that the JsonABI object cannot be creating during type checking.

* More.

* Print json to outfile.

* Actually output JSON.

* Delete unused function.

* JSON captures components field.

* Basic testing infra.

* Actual oracle values.

* Format.

* Split compile_asm into compile_ast.

* Finish merge.

* nits

* Fix bad merge.

* Update oracles.

* Missing oracle files.

* fmt

* rename function.

* fix bug

* Fix manifest_dir bug.

* add missing oracle files
  • Loading branch information
emilyaherbert authored Dec 14, 2021
1 parent 5418088 commit a3617ef
Show file tree
Hide file tree
Showing 98 changed files with 931 additions and 258 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# Generated by Cargo
# will have compiled files and executables
**/*/target/
**/*/json_abi_output.json
target

# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries
Expand Down
43 changes: 43 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 1 addition & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,7 @@ members = [
"docstrings",
"forc",
"formatter",
"parser"
,
"parser",
"sway-server",
"test"]

Expand Down
4 changes: 4 additions & 0 deletions core_lang/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,10 @@ smallvec = "1.7"
structopt = { version = "0.3", default-features = false, optional = true }
thiserror = "1.0"
uuid-b64 = "0.1"
line-col = "0.2"
source-span = "2.4"
core-types = { path = "../core-types" }


[[bin]]
name = "selector-debug"
Expand Down
61 changes: 61 additions & 0 deletions core_lang/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,11 @@ use crate::span::Span;
use crate::style::{to_screaming_snake_case, to_snake_case, to_upper_camel_case};
use crate::type_engine::*;
use crate::type_engine::{IntegerBits, TypeInfo};
use line_col::LineColLookup;
use source_span::{
fmt::{Formatter, Style},
Position,
};
use std::fmt;
use thiserror::Error;

Expand Down Expand Up @@ -184,6 +189,34 @@ impl<'sc> CompileWarning<'sc> {
self.span.end_pos().line_col().into(),
)
}

pub fn format(&self, fmt: &mut Formatter) -> source_span::fmt::Formatted {
let input = self.span.input();
let chars = input.chars().map(|x| -> Result<_, ()> { Ok(x) });

let metrics = source_span::DEFAULT_METRICS;
let buffer = source_span::SourceBuffer::new(chars, Position::default(), metrics);

for c in buffer.iter() {
let _ = c.unwrap(); // report eventual errors.
}

let (start_pos, end_pos) = self.span();
let lookup = LineColLookup::new(input);
let (start_line, start_col) = lookup.get(start_pos);
let (end_line, end_col) = lookup.get(end_pos - 1);

let err_start = Position::new(start_line - 1, start_col - 1);
let err_end = Position::new(end_line - 1, end_col - 1);
let err_span = source_span::Span::new(err_start, err_end, err_end.next_column());
fmt.add(
err_span,
Some(self.to_friendly_warning_string()),
Style::Warning,
);

fmt.render(buffer.iter(), buffer.span(), &metrics).unwrap()
}
}

#[derive(Debug, Clone, PartialEq, Hash)]
Expand Down Expand Up @@ -977,4 +1010,32 @@ impl<'sc> CompileError<'sc> {
self.internal_span().end_pos().line_col().into(),
)
}

pub fn format(&self, fmt: &mut Formatter) -> source_span::fmt::Formatted {
let input = self.internal_span().input();
let chars = input.chars().map(Result::<_, String>::Ok);

let metrics = source_span::DEFAULT_METRICS;
let buffer = source_span::SourceBuffer::new(chars, Position::default(), metrics);

for c in buffer.iter() {
let _ = c.unwrap(); // report eventual errors.
}

let (start_pos, end_pos) = self.span();
let lookup = LineColLookup::new(input);
let (start_line, start_col) = lookup.get(start_pos);
let (end_line, end_col) = lookup.get(if end_pos == 0 { 0 } else { end_pos - 1 });

let err_start = Position::new(start_line - 1, start_col - 1);
let err_end = Position::new(end_line - 1, end_col - 1);
let err_span = source_span::Span::new(err_start, err_end, err_end.next_column());
fmt.add(
err_span,
Some(self.to_friendly_error_string()),
Style::Error,
);

fmt.render(buffer.iter(), buffer.span(), &metrics).unwrap()
}
}
90 changes: 64 additions & 26 deletions core_lang/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ use pest::Parser;
use std::collections::{HashMap, HashSet};

pub use semantic_analysis::TreeType;
use semantic_analysis::TypedParseTree;
pub use semantic_analysis::TypedParseTree;
pub mod types;
pub(crate) mod utils;
pub use crate::parse_tree::{Declaration, Expression, UseStatement, WhileLoop};
Expand Down Expand Up @@ -177,6 +177,18 @@ pub enum CompilationResult<'sc> {
},
}

pub enum CompileAstResult<'sc> {
Success {
parse_tree: TypedParseTree<'sc>,
tree_type: TreeType<'sc>,
warnings: Vec<CompileWarning<'sc>>,
},
Failure {
warnings: Vec<CompileWarning<'sc>>,
errors: Vec<CompileError<'sc>>,
},
}

/// Represents the result of compiling Sway code via `compile_to_bytecode`.
/// Contains the compiled bytecode in byte form, or, resulting errors, and any warnings generated.
pub enum BytecodeCompilationResult<'sc> {
Expand Down Expand Up @@ -297,19 +309,17 @@ pub(crate) fn compile_inner_dependency<'sc>(
)
}

/// Given input Sway source code, compile to a [CompilationResult] which contains the asm in opcode
/// form (not raw bytes/bytecode).
pub fn compile_to_asm<'sc>(
pub fn compile_to_ast<'sc>(
input: &'sc str,
initial_namespace: &Namespace<'sc>,
build_config: BuildConfig,
build_config: &BuildConfig,
dependency_graph: &mut HashMap<String, HashSet<String>>,
) -> CompilationResult<'sc> {
) -> CompileAstResult<'sc> {
let mut warnings = Vec::new();
let mut errors = Vec::new();
let parse_tree = check!(
parse(input, Some(&build_config)),
return CompilationResult::Failure { errors, warnings },
parse(input, Some(build_config)),
return CompileAstResult::Failure { errors, warnings },
warnings,
errors
);
Expand All @@ -328,7 +338,7 @@ pub fn compile_to_asm<'sc>(
&mut dead_code_graph,
dependency_graph,
),
return CompilationResult::Failure { errors, warnings },
return CompileAstResult::Failure { errors, warnings },
warnings,
errors
);
Expand All @@ -345,26 +355,54 @@ pub fn compile_to_asm<'sc>(
warnings = dedup_unsorted(warnings);

if !errors.is_empty() {
return CompilationResult::Failure { errors, warnings };
return CompileAstResult::Failure { errors, warnings };
}
match parse_tree.tree_type {
TreeType::Contract | TreeType::Script | TreeType::Predicate => {
let asm = check!(
compile_ast_to_asm(typed_parse_tree, &build_config),
return CompilationResult::Failure { errors, warnings },
warnings,
errors
);
if !errors.is_empty() {
return CompilationResult::Failure { errors, warnings };

CompileAstResult::Success {
parse_tree: typed_parse_tree,
tree_type: parse_tree.tree_type,
warnings,
}
}

/// Given input Sway source code, compile to a [CompilationResult] which contains the asm in opcode
/// form (not raw bytes/bytecode).
pub fn compile_to_asm<'sc>(
input: &'sc str,
initial_namespace: &Namespace<'sc>,
build_config: BuildConfig,
dependency_graph: &mut HashMap<String, HashSet<String>>,
) -> CompilationResult<'sc> {
match compile_to_ast(input, initial_namespace, &build_config, dependency_graph) {
CompileAstResult::Failure { warnings, errors } => {
CompilationResult::Failure { warnings, errors }
}
CompileAstResult::Success {
parse_tree,
tree_type,
mut warnings,
} => {
let mut errors = vec![];
match tree_type {
TreeType::Contract | TreeType::Script | TreeType::Predicate => {
let asm = check!(
compile_ast_to_asm(parse_tree, &build_config),
return CompilationResult::Failure { errors, warnings },
warnings,
errors
);
if !errors.is_empty() {
return CompilationResult::Failure { errors, warnings };
}
CompilationResult::Success { asm, warnings }
}
TreeType::Library { name } => CompilationResult::Library {
warnings,
name,
namespace: parse_tree.into_namespace(),
},
}
CompilationResult::Success { asm, warnings }
}
TreeType::Library { name } => CompilationResult::Library {
warnings,
name,
namespace: typed_parse_tree.into_namespace(),
},
}
}

Expand Down
22 changes: 22 additions & 0 deletions core_lang/src/parse_tree/declaration/function.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ use crate::span::Span;
use crate::style::is_snake_case;
use crate::type_engine::TypeInfo;
use crate::{CodeBlock, Ident, Rule};
use core_types::{Function, Property};
use pest::iterators::Pair;

#[derive(Debug, Clone)]
Expand Down Expand Up @@ -187,6 +188,27 @@ impl<'sc> FunctionDeclaration<'sc> {
errors,
)
}

pub fn parse_json_abi(&self) -> Function {
Function {
name: self.name.primary_name.to_string(),
type_field: "function".to_string(),
inputs: self
.parameters
.iter()
.map(|x| Property {
name: x.name.primary_name.to_string(),
type_field: x.r#type.friendly_type_str(),
components: None,
})
.collect(),
outputs: vec![Property {
name: "".to_string(),
type_field: self.return_type.friendly_type_str(),
components: None,
}],
}
}
}

#[derive(Debug, Clone, PartialEq, Eq)]
Expand Down
2 changes: 1 addition & 1 deletion core_lang/src/semantic_analysis.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,6 @@ pub(crate) use ast_node::{TypedAstNode, TypedAstNodeContent, TypedExpression};
pub use ast_node::{TypedConstantDeclaration, TypedDeclaration, TypedFunctionDeclaration};
pub use namespace::Namespace;
pub use syntax_tree::TreeType;
pub(crate) use syntax_tree::TypedParseTree;
pub use syntax_tree::TypedParseTree;

const ERROR_RECOVERY_DECLARATION: TypedDeclaration = TypedDeclaration::ErrorRecovery;
Loading

0 comments on commit a3617ef

Please sign in to comment.