Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Disallow top-level lets and introduce const declarations. #278

Merged
merged 5 commits into from
Sep 29, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions core_lang/src/asm_generation/declaration/const_decl.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
use crate::{
asm_generation::{convert_expression_to_asm, AsmNamespace, RegisterSequencer},
asm_lang::Op,
error::*,
semantic_analysis::ast_node::TypedConstantDeclaration,
};

/// Provisions a register to put a value in, and then adds the assembly used to initialize the
/// value to the end of the buffer.
pub(crate) fn convert_constant_decl_to_asm<'sc>(
const_decl: &TypedConstantDeclaration<'sc>,
namespace: &mut AsmNamespace<'sc>,
register_sequencer: &mut RegisterSequencer,
) -> CompileResult<'sc, Vec<Op<'sc>>> {
let val_register = register_sequencer.next();
let initialization = convert_expression_to_asm(
&const_decl.value,
namespace,
&val_register,
register_sequencer,
);
namespace.insert_variable(const_decl.name.clone(), val_register);
initialization
}
8 changes: 7 additions & 1 deletion core_lang/src/asm_generation/declaration/mod.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
use super::{AsmNamespace, RegisterSequencer};
use crate::{asm_lang::Op, error::*, TypedDeclaration};

use super::{AsmNamespace, RegisterSequencer};
mod const_decl;
mod fn_decl;
mod reassignment;
mod var_decl;

pub(crate) use const_decl::convert_constant_decl_to_asm;
pub(crate) use fn_decl::convert_fn_decl_to_asm;
pub(crate) use reassignment::convert_reassignment_to_asm;
pub(crate) use var_decl::convert_variable_decl_to_asm;
Expand All @@ -29,6 +32,9 @@ pub(crate) fn convert_decl_to_asm<'sc>(
TypedDeclaration::VariableDeclaration(var_decl) => {
convert_variable_decl_to_asm(var_decl, namespace, register_sequencer)
}
TypedDeclaration::ConstantDeclaration(const_decl) => {
convert_constant_decl_to_asm(const_decl, namespace, register_sequencer)
}
TypedDeclaration::Reassignment(reassignment) => {
convert_reassignment_to_asm(reassignment, namespace, register_sequencer)
}
Expand Down
76 changes: 72 additions & 4 deletions core_lang/src/asm_generation/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@ use crate::{
error::*,
parse_tree::Literal,
semantic_analysis::{
TypedAstNode, TypedAstNodeContent, TypedFunctionDeclaration, TypedParseTree,
TypedAstNode, TypedAstNodeContent, TypedDeclaration, TypedFunctionDeclaration,
TypedParseTree,
},
types::{MaybeResolvedType, ResolvedType},
BuildConfig, Ident,
Expand Down Expand Up @@ -626,9 +627,24 @@ pub(crate) fn compile_ast_to_asm<'sc>(
let mut warnings = vec![];
let mut errors = vec![];
let asm = match ast {
TypedParseTree::Script { main_function, .. } => {
TypedParseTree::Script {
main_function,
declarations,
..
} => {
let mut namespace: AsmNamespace = Default::default();
let mut asm_buf = build_preamble(&mut register_sequencer).to_vec();
check!(
add_global_constant_decls(
&mut namespace,
&mut register_sequencer,
&mut asm_buf,
&declarations
),
return err(warnings, errors),
warnings,
errors
);
// start generating from the main function
let return_register = register_sequencer.next();
let mut body = check!(
Expand Down Expand Up @@ -661,9 +677,24 @@ pub(crate) fn compile_ast_to_asm<'sc>(
data_section: namespace.data_section,
}
}
TypedParseTree::Predicate { main_function, .. } => {
TypedParseTree::Predicate {
main_function,
declarations,
..
} => {
let mut namespace: AsmNamespace = Default::default();
let mut asm_buf = build_preamble(&mut register_sequencer).to_vec();
check!(
add_global_constant_decls(
&mut namespace,
&mut register_sequencer,
&mut asm_buf,
&declarations
),
return err(warnings, errors),
warnings,
errors
);
// start generating from the main function
let mut body = check!(
convert_code_block_to_asm(
Expand All @@ -683,9 +714,24 @@ pub(crate) fn compile_ast_to_asm<'sc>(
data_section: namespace.data_section,
}
}
TypedParseTree::Contract { abi_entries, .. } => {
TypedParseTree::Contract {
abi_entries,
declarations,
..
} => {
let mut namespace: AsmNamespace = Default::default();
let mut asm_buf = build_preamble(&mut register_sequencer).to_vec();
check!(
add_global_constant_decls(
&mut namespace,
&mut register_sequencer,
&mut asm_buf,
&declarations
),
return err(warnings, errors),
warnings,
errors
);
let (selectors_and_labels, mut contract_asm) = check!(
compile_contract_to_selectors(abi_entries, &mut namespace, &mut register_sequencer),
return err(warnings, errors),
Expand Down Expand Up @@ -1128,6 +1174,28 @@ fn build_contract_abi_switch<'sc>(
asm_buf
}

fn add_global_constant_decls<'sc>(
namespace: &mut AsmNamespace<'sc>,
register_sequencer: &mut RegisterSequencer,
asm_buf: &mut Vec<Op<'sc>>,
declarations: &[TypedDeclaration<'sc>],
) -> CompileResult<'sc, ()> {
let mut warnings = vec![];
let mut errors = vec![];
for declaration in declarations {
if let TypedDeclaration::ConstantDeclaration(decl) = declaration {
let mut ops = check!(
convert_constant_decl_to_asm(&decl, namespace, register_sequencer),
return err(warnings, errors),
warnings,
errors
);
asm_buf.append(&mut ops);
}
}
ok((), warnings, errors)
}

/// Given a contract's abi entries, compile them to jump destinations and an opcode buffer.
fn compile_contract_to_selectors<'sc>(
abi_entries: Vec<TypedFunctionDeclaration<'sc>>,
Expand Down
4 changes: 2 additions & 2 deletions core_lang/src/control_flow_analysis/analyze_return_paths.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use crate::parse_tree::CallPath;
use crate::semantic_analysis::{
ast_node::{
TypedCodeBlock, TypedDeclaration, TypedExpression, TypedFunctionDeclaration,
TypedReassignment, TypedVariableDeclaration, TypedWhileLoop,
TypedReassignment, TypedWhileLoop,
},
TypedAstNode, TypedAstNodeContent,
};
Expand Down Expand Up @@ -180,7 +180,7 @@ fn connect_declaration<'sc>(
TraitDeclaration(_) | AbiDeclaration(_) | StructDeclaration(_) | EnumDeclaration(_) => {
leaves.to_vec()
}
VariableDeclaration(TypedVariableDeclaration { .. }) => {
VariableDeclaration(_) | ConstantDeclaration(_) => {
let entry_node = graph.add_node(node.into());
for leaf in leaves {
graph.add_edge(*leaf, entry_node, "".into());
Expand Down
24 changes: 21 additions & 3 deletions core_lang/src/control_flow_analysis/dead_code_analysis.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,9 @@ use crate::{
use crate::{
semantic_analysis::{
ast_node::{
TypedCodeBlock, TypedDeclaration, TypedEnumDeclaration, TypedExpression,
TypedFunctionDeclaration, TypedReassignment, TypedVariableDeclaration, TypedWhileLoop,
TypedCodeBlock, TypedConstantDeclaration, TypedDeclaration, TypedEnumDeclaration,
TypedExpression, TypedFunctionDeclaration, TypedReassignment, TypedVariableDeclaration,
TypedWhileLoop,
},
TypedAstNode, TypedAstNodeContent, TypedParseTree,
},
Expand Down Expand Up @@ -344,6 +345,10 @@ fn connect_declaration<'sc>(
tree_type,
body.clone().span,
),
ConstantDeclaration(TypedConstantDeclaration { name, .. }) => {
graph.namespace.insert_constant(name.clone(), entry_node);
Ok(leaves.to_vec())
}
FunctionDeclaration(fn_decl) => {
connect_typed_fn_decl(fn_decl, graph, entry_node, span, exit_node, tree_type)?;
Ok(leaves.to_vec())
Expand Down Expand Up @@ -692,7 +697,20 @@ fn connect_expression<'sc>(
}
Ok(vec![node])
}
VariableExpression { .. } => Ok(leaves.to_vec()),
VariableExpression { name, .. } => {
// Variables may refer to global const declarations.
Ok(graph
.namespace
.get_constant(name)
.cloned()
.map(|node| {
for leaf in leaves {
graph.add_edge(*leaf, node, "".into());
}
vec![node]
})
.unwrap_or_else(|| leaves.to_vec()))
}
EnumInstantiation {
enum_decl,
variant_name,
Expand Down
7 changes: 7 additions & 0 deletions core_lang/src/control_flow_analysis/flow_graph/namespace.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ pub struct ControlFlowNamespace<'sc> {
pub(crate) trait_method_namespace: HashMap<CallPath<'sc>, HashMap<Ident<'sc>, NodeIndex>>,
/// This is a mapping from struct name to field names and their node indexes
pub(crate) struct_namespace: HashMap<Ident<'sc>, StructNamespaceEntry<'sc>>,
pub(crate) const_namespace: HashMap<Ident<'sc>, NodeIndex>,
}

impl<'sc> ControlFlowNamespace<'sc> {
Expand All @@ -48,6 +49,12 @@ impl<'sc> ControlFlowNamespace<'sc> {
) {
self.function_namespace.insert(ident, entry);
}
pub(crate) fn get_constant(&self, ident: &Ident<'sc>) -> Option<&NodeIndex> {
self.const_namespace.get(ident)
}
pub(crate) fn insert_constant(&mut self, const_name: Ident<'sc>, declaration_node: NodeIndex) {
self.const_namespace.insert(const_name, declaration_node);
}
pub(crate) fn insert_enum(
&mut self,
enum_name: Ident<'sc>,
Expand Down
19 changes: 11 additions & 8 deletions core_lang/src/hll.pest
Original file line number Diff line number Diff line change
Expand Up @@ -24,15 +24,16 @@ ref_keyword = {"ref "}
deref_keyword = {"deref "}
true_keyword = {"true"}
false_keyword = {"false"}
const_decl_keyword = {"const"}

// top level
program = {SOI ~ (library|contract|script|predicate)? ~ EOI}

library = {"library" ~ library_name ~ ";" ~ (control_flow|declaration|use_statement|include_statement)* }
library = {"library" ~ library_name ~ ";" ~ (non_var_decl|use_statement|include_statement)* }
library_name = {ident}
contract = {"contract" ~ ";" ~ (control_flow|declaration|use_statement|include_statement)*}
script = {"script" ~ ";" ~ (control_flow|declaration|use_statement|include_statement)*}
predicate = {"predicate" ~ ";" ~ (control_flow|declaration|use_statement|include_statement)*}
contract = {"contract" ~ ";" ~ (non_var_decl|use_statement|include_statement)*}
script = {"script" ~ ";" ~ (non_var_decl|use_statement|include_statement)*}
predicate = {"predicate" ~ ";" ~ (non_var_decl|use_statement|include_statement)*}

// including other files
file_path = { ident ~ ("/" ~ ident)* }
Expand All @@ -52,7 +53,7 @@ var_name_ident = {ident}
struct_field_access = {subfield_path}
method_exp = {subfield_exp | fully_qualified_method}
subfield_exp = {subfield_path ~ fn_args}
// TODO subfield path should allow parenthesized expressions _or_ just idents
// TODO subfield path should allow parenthesized expressions _or_ just idents
subfield_path = {(call_item ~ ".")+ ~ call_item}
fully_qualified_method = {path_separator? ~ (path_ident ~ path_separator)* ~ "~" ~ type_name ~ path_separator ~ call_item ~ fn_args}
call_item = {ident | "(" ~ expr ~ ")" }
Expand All @@ -62,8 +63,8 @@ path_ident = {ident}

// abi blocks and abi casting
abi_cast = {abi_keyword ~ "(" ~ trait_name ~ "," ~ expr ~ ")"}
abi_decl = {abi_keyword ~ abi_name ~ trait_methods}
abi_name = {ident}
abi_decl = {abi_keyword ~ abi_name ~ trait_methods}
abi_name = {ident}


if_exp = {"if" ~ expr ~ code_block ~ ("else" ~ (code_block|if_exp))?}
Expand Down Expand Up @@ -100,7 +101,8 @@ struct_expr_fields = {struct_field_name ~ ":" ~ expr ~ ("," ~ struct_field_name
array_exp = {"[" ~ (expr ~ ("," ~ expr)*) ~ "]"}

// declarations
declaration = {docstring|enum_decl|var_decl|fn_decl|trait_decl|abi_decl|struct_decl|reassignment|impl_trait|impl_self}
declaration = {(non_var_decl|var_decl|reassignment)}
non_var_decl = {(docstring|enum_decl|fn_decl|trait_decl|abi_decl|struct_decl|impl_trait|impl_self|const_decl)}
var_decl = {var_decl_keyword ~ mut_keyword? ~ var_name ~ type_ascription? ~ assign ~ expr ~ ";"}
type_ascription = {":" ~ type_name}
fn_decl = {docstring? ~ visibility ~ fn_signature ~ code_block}
Expand All @@ -109,6 +111,7 @@ var_name = {ident}
reassignment = {variable_reassignment | struct_field_reassignment}
variable_reassignment = {var_exp ~ assign ~ expr ~ ";"}
struct_field_reassignment = {struct_field_access ~ assign ~ expr ~ ";" }
const_decl = {const_decl_keyword ~ var_name ~ type_ascription? ~ assign ~ literal_value ~ ";"}

visibility = {"pub"?}

Expand Down
4 changes: 2 additions & 2 deletions core_lang/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -549,7 +549,7 @@ fn parse_root_from_pairs<'sc>(
let mut library_name = None;
for pair in input {
match pair.as_rule() {
Rule::declaration => {
Rule::non_var_decl => {
let mut decl = pair.clone().into_inner();
let decl_inner = decl.next().unwrap();
match decl_inner.as_rule() {
Expand All @@ -561,7 +561,7 @@ fn parse_root_from_pairs<'sc>(
}
_ => {
let decl = check!(
Declaration::parse_from_pair(
Declaration::parse_non_var_from_pair(
pair.clone(),
config,
unassigned_docstring.clone(),
Expand Down
67 changes: 67 additions & 0 deletions core_lang/src/parse_tree/declaration/constant_declaration.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
use crate::parse_tree::Expression;
use crate::{types::TypeInfo, Ident};

use crate::build_config::BuildConfig;
use crate::error::{err, ok, CompileResult};
use crate::parser::Rule;
use pest::iterators::Pair;

use std::collections::HashMap;

#[derive(Debug, Clone)]
pub struct ConstantDeclaration<'sc> {
pub name: Ident<'sc>,
pub type_ascription: Option<TypeInfo<'sc>>,
pub value: Expression<'sc>,
}

impl<'sc> ConstantDeclaration<'sc> {
pub(crate) fn parse_from_pair(
pair: Pair<'sc, Rule>,
config: Option<&BuildConfig>,
docstrings: &mut HashMap<String, String>,
) -> CompileResult<'sc, ConstantDeclaration<'sc>> {
let mut warnings = Vec::new();
let mut errors = Vec::new();
let mut const_decl_parts = pair.into_inner();
let _const_keyword = const_decl_parts.next();
let name_pair = const_decl_parts.next().unwrap();
let mut maybe_value = const_decl_parts.next().unwrap();
let type_ascription = match maybe_value.as_rule() {
Rule::type_ascription => {
let type_asc = maybe_value.clone();
maybe_value = const_decl_parts.next().unwrap();
Some(type_asc)
}
_ => None,
};
let type_ascription = type_ascription.map(|ascription| {
check!(
TypeInfo::parse_from_pair(ascription, config.clone()),
TypeInfo::Unit,
warnings,
errors
)
});
let value = check!(
Expression::parse_from_pair_inner(maybe_value, config.clone(), docstrings),
return err(warnings, errors),
warnings,
errors
);
ok(
ConstantDeclaration {
name: check!(
Ident::parse_from_pair(name_pair, config.clone()),
return err(warnings, errors),
warnings,
errors
),
type_ascription,
value,
},
warnings,
errors,
)
}
}
Loading