Skip to content

Commit

Permalink
Lowering pass and augmented assignments.
Browse files Browse the repository at this point in the history
  • Loading branch information
g-r-a-n-t committed Mar 29, 2021
1 parent dd017d8 commit 815e4bf
Show file tree
Hide file tree
Showing 19 changed files with 452 additions and 17 deletions.
34 changes: 34 additions & 0 deletions analyzer/src/traversal/assignments.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@ use crate::namespace::scopes::{
BlockScope,
Shared,
};
use crate::namespace::types::{
Base,
Type,
};
use crate::traversal::expressions;
use crate::Context;
use crate::Location;
Expand Down Expand Up @@ -50,6 +54,36 @@ pub fn assign(
unreachable!()
}

/// Gather context information for assignments and check for type errors.
pub fn aug_assign(
scope: Shared<BlockScope>,
context: Shared<Context>,
stmt: &Node<fe::FuncStmt>,
) -> Result<(), SemanticError> {
if let fe::FuncStmt::AugAssign {
target,
op: _,
value,
} = &stmt.kind
{
let target_attributes = expressions::expr(Rc::clone(&scope), Rc::clone(&context), target)?;
let value_attributes = expressions::expr(scope, context, value)?;

return match target_attributes.typ {
Type::Base(Base::Numeric(_)) => {
if target_attributes.typ == value_attributes.typ {
Ok(())
} else {
Err(SemanticError::type_error())
}
}
_ => Err(SemanticError::type_error()),
};
}

unreachable!()
}

#[cfg(test)]
mod tests {
use crate::errors::{
Expand Down
2 changes: 1 addition & 1 deletion analyzer/src/traversal/functions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -185,7 +185,7 @@ fn func_stmt(
fe::FuncStmt::VarDecl { .. } => declarations::var_decl(scope, context, stmt),
fe::FuncStmt::Assign { .. } => assignments::assign(scope, context, stmt),
fe::FuncStmt::Emit { .. } => emit(scope, context, stmt),
fe::FuncStmt::AugAssign { .. } => unimplemented!(),
fe::FuncStmt::AugAssign { .. } => assignments::aug_assign(scope, context, stmt),
fe::FuncStmt::For { .. } => for_loop(scope, context, stmt),
fe::FuncStmt::While { .. } => while_loop(scope, context, stmt),
fe::FuncStmt::If { .. } => if_statement(scope, context, stmt),
Expand Down
7 changes: 4 additions & 3 deletions compiler/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ pub fn compile(
let json_abis = abi::build(&context, &fe_module)?;

// lower the AST
let lowered_fe_module = lowering::lower(&context, &fe_module);
let lowered_fe_module = lowering::lower(&context, fe_module.clone());

// analyze the lowered AST
let context = fe_analyzer::analyze(&lowered_fe_module)
Expand Down Expand Up @@ -81,8 +81,9 @@ pub fn compile(
.collect::<NamedContracts>();

Ok(CompiledModule {
fe_tokens: format!("{:#?}", fe_tokens),
fe_ast: format!("{:#?}", fe_module),
src_tokens: format!("{:#?}", fe_tokens),
src_ast: format!("{:#?}", fe_module),
lowered_ast: format!("{:#?}", lowered_fe_module),
contracts,
})
}
1 change: 0 additions & 1 deletion compiler/src/lowering/mappers/assignments.rs

This file was deleted.

29 changes: 29 additions & 0 deletions compiler/src/lowering/mappers/contracts.rs
Original file line number Diff line number Diff line change
@@ -1 +1,30 @@
use fe_analyzer::Context;

use crate::lowering::mappers::functions;
use fe_parser::ast as fe;
use fe_parser::ast::ContractStmt;
use fe_parser::node::Node;

/// Lowers a contract definition.
pub fn contract_def(context: &Context, stmt: Node<fe::ModuleStmt>) -> Node<fe::ModuleStmt> {
if let fe::ModuleStmt::ContractDef { name, body } = stmt.kind {
let lowered_body = body
.into_iter()
.map(|stmt| match stmt.kind {
ContractStmt::ContractField { .. } => stmt,
ContractStmt::EventDef { .. } => stmt,
ContractStmt::FuncDef { .. } => functions::func_def(context, stmt),
})
.collect();

return Node::new(
fe::ModuleStmt::ContractDef {
name,
body: lowered_body,
},
stmt.span,
);
}

unreachable!()
}
1 change: 0 additions & 1 deletion compiler/src/lowering/mappers/declarations.rs

This file was deleted.

122 changes: 122 additions & 0 deletions compiler/src/lowering/mappers/expressions.rs
Original file line number Diff line number Diff line change
@@ -1 +1,123 @@
use fe_analyzer::Context;
use fe_parser::ast as fe;
use fe_parser::node::Node;

/// Lowers an expression and all sub expressions.
pub fn expr(context: &Context, exp: Node<fe::Expr>) -> Node<fe::Expr> {
let lowered_kind = match exp.kind {
fe::Expr::Name(_) => exp.kind,
fe::Expr::Num(_) => exp.kind,
fe::Expr::Bool(_) => exp.kind,
fe::Expr::Subscript { value, slices } => fe::Expr::Subscript {
value: boxed_expr(context, value),
slices: slices_index_expr(context, slices),
},
fe::Expr::Attribute { value, attr } => fe::Expr::Attribute {
value: boxed_expr(context, value),
attr,
},
fe::Expr::Ternary {
if_expr,
test,
else_expr,
} => fe::Expr::Ternary {
if_expr: boxed_expr(context, if_expr),
test: boxed_expr(context, test),
else_expr: boxed_expr(context, else_expr),
},
fe::Expr::BoolOperation { left, op, right } => fe::Expr::BoolOperation {
left: boxed_expr(context, left),
op,
right: boxed_expr(context, right),
},
fe::Expr::BinOperation { left, op, right } => fe::Expr::BinOperation {
left: boxed_expr(context, left),
op,
right: boxed_expr(context, right),
},
fe::Expr::UnaryOperation { op, operand } => fe::Expr::UnaryOperation {
op,
operand: boxed_expr(context, operand),
},
fe::Expr::CompOperation { left, op, right } => fe::Expr::CompOperation {
left: boxed_expr(context, left),
op,
right: boxed_expr(context, right),
},
fe::Expr::Call { func, args } => fe::Expr::Call {
func: boxed_expr(context, func),
args: call_args(context, args),
},
fe::Expr::List { .. } => unimplemented!(),
fe::Expr::ListComp { .. } => unimplemented!(),
// We only accept empty tuples for now. We may want to completely eliminate tuple
// expressions before the Yul codegen pass, tho.
fe::Expr::Tuple { .. } => exp.kind,
fe::Expr::Str(_) => exp.kind,
fe::Expr::Ellipsis => unimplemented!(),
};

Node::new(lowered_kind, exp.span)
}

fn slices_index_expr(
context: &Context,
slices: Node<Vec<Node<fe::Slice>>>,
) -> Node<Vec<Node<fe::Slice>>> {
let first_slice = &slices.kind[0];

if let fe::Slice::Index(exp) = &first_slice.kind {
return Node::new(
vec![Node::new(
fe::Slice::Index(Box::new(expr(context, *exp.to_owned()))),
first_slice.span,
)],
slices.span,
);
}

unreachable!()
}

/// Lowers and optional expression.
pub fn optional_expr(context: &Context, exp: Option<Node<fe::Expr>>) -> Option<Node<fe::Expr>> {
exp.map(|exp| expr(context, exp))
}

/// Lowers a boxed expression.
#[allow(clippy::boxed_local)]
pub fn boxed_expr(context: &Context, exp: Box<Node<fe::Expr>>) -> Box<Node<fe::Expr>> {
Box::new(expr(context, *exp))
}

/// Lowers a list of expression.
pub fn multiple_exprs(context: &Context, exp: Vec<Node<fe::Expr>>) -> Vec<Node<fe::Expr>> {
exp.into_iter().map(|exp| expr(context, exp)).collect()
}

fn call_args(
context: &Context,
args: Node<Vec<Node<fe::CallArg>>>,
) -> Node<Vec<Node<fe::CallArg>>> {
let lowered_args = args
.kind
.into_iter()
.map(|arg| match arg.kind {
fe::CallArg::Arg(inner_arg) => {
Node::new(fe::CallArg::Arg(expr(context, inner_arg)), arg.span)
}
fe::CallArg::Kwarg(inner_arg) => {
Node::new(fe::CallArg::Kwarg(kwarg(context, inner_arg)), arg.span)
}
})
.collect();

Node::new(lowered_args, args.span)
}

fn kwarg(context: &Context, kwarg: fe::Kwarg) -> fe::Kwarg {
fe::Kwarg {
name: kwarg.name,
value: boxed_expr(context, kwarg.value),
}
}
129 changes: 129 additions & 0 deletions compiler/src/lowering/mappers/functions.rs
Original file line number Diff line number Diff line change
@@ -1 +1,130 @@
use crate::lowering::mappers::expressions;
use fe_analyzer::Context;
use fe_parser::ast as fe;
use fe_parser::node::Node;

/// Lowers a function definition.
pub fn func_def(context: &Context, def: Node<fe::ContractStmt>) -> Node<fe::ContractStmt> {
if let fe::ContractStmt::FuncDef {
qual,
name,
args,
return_type,
body,
} = def.kind
{
let lowered_body = multiple_stmts(context, body);

let lowered_kind = fe::ContractStmt::FuncDef {
qual,
name,
args,
return_type,
body: lowered_body,
};

return Node::new(lowered_kind, def.span);
}

unreachable!()
}

fn func_stmt(context: &Context, stmt: Node<fe::FuncStmt>) -> Vec<Node<fe::FuncStmt>> {
let lowered_kinds = match stmt.kind {
fe::FuncStmt::Return { value } => vec![fe::FuncStmt::Return {
value: expressions::optional_expr(context, value),
}],
fe::FuncStmt::VarDecl { target, typ, value } => vec![fe::FuncStmt::VarDecl {
target: expressions::expr(context, target),
typ,
value: expressions::optional_expr(context, value),
}],
fe::FuncStmt::Assign { targets, value } => vec![fe::FuncStmt::Assign {
targets: expressions::multiple_exprs(context, targets),
value: expressions::expr(context, value),
}],
fe::FuncStmt::Emit { value } => vec![fe::FuncStmt::Emit {
value: expressions::expr(context, value),
}],
fe::FuncStmt::AugAssign { target, op, value } => aug_assign(context, target, op, value),
fe::FuncStmt::For {
target,
iter,
body,
or_else,
} => vec![fe::FuncStmt::For {
target: expressions::expr(context, target),
iter: expressions::expr(context, iter),
body: multiple_stmts(context, body),
or_else: multiple_stmts(context, or_else),
}],
fe::FuncStmt::While {
test,
body,
or_else,
} => vec![fe::FuncStmt::While {
test: expressions::expr(context, test),
body: multiple_stmts(context, body),
or_else: multiple_stmts(context, or_else),
}],
fe::FuncStmt::If {
test,
body,
or_else,
} => vec![fe::FuncStmt::If {
test: expressions::expr(context, test),
body: multiple_stmts(context, body),
or_else: multiple_stmts(context, or_else),
}],
fe::FuncStmt::Assert { test, msg } => vec![fe::FuncStmt::Assert {
test: expressions::expr(context, test),
msg: expressions::optional_expr(context, msg),
}],
fe::FuncStmt::Expr { value } => vec![fe::FuncStmt::Expr {
value: expressions::expr(context, value),
}],
fe::FuncStmt::Pass => vec![stmt.kind],
fe::FuncStmt::Break => vec![stmt.kind],
fe::FuncStmt::Continue => vec![stmt.kind],
fe::FuncStmt::Revert => vec![stmt.kind],
};
let span = stmt.span;

lowered_kinds
.into_iter()
.map(|kind| Node::new(kind, span))
.collect()
}

fn multiple_stmts(context: &Context, stmts: Vec<Node<fe::FuncStmt>>) -> Vec<Node<fe::FuncStmt>> {
stmts
.into_iter()
.map(|stmt| func_stmt(context, stmt))
.collect::<Vec<Vec<Node<fe::FuncStmt>>>>()
.concat()
}

fn aug_assign(
context: &Context,
target: Node<fe::Expr>,
op: Node<fe::BinOperator>,
value: Node<fe::Expr>,
) -> Vec<fe::FuncStmt> {
let lowered_target = expressions::expr(context, target);
let original_value_span = value.span;
let lowered_value = expressions::expr(context, value);

let new_value_kind = fe::Expr::BinOperation {
left: Box::new(lowered_target.clone().new_id()),
op,
right: Box::new(lowered_value),
};

let new_value = Node::new(new_value_kind, original_value_span);

// the new statement is: `target = target <op> value`.
vec![fe::FuncStmt::Assign {
targets: vec![lowered_target],
value: new_value,
}]
}
2 changes: 0 additions & 2 deletions compiler/src/lowering/mappers/mod.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
mod assignments;
mod contracts;
mod declarations;
mod expressions;
mod functions;
pub mod module;
20 changes: 20 additions & 0 deletions compiler/src/lowering/mappers/module.rs
Original file line number Diff line number Diff line change
@@ -1 +1,21 @@
use fe_analyzer::Context;

use crate::lowering::mappers::contracts;
use fe_parser::ast as fe;

/// Lowers a module.
pub fn module(context: &Context, module: fe::Module) -> fe::Module {
let lowered_body = module
.body
.into_iter()
.map(|stmt| match &stmt.kind {
fe::ModuleStmt::TypeDef { .. } => stmt,
fe::ModuleStmt::StructDef { .. } => stmt,
fe::ModuleStmt::FromImport { .. } => stmt,
fe::ModuleStmt::SimpleImport { .. } => stmt,
fe::ModuleStmt::ContractDef { .. } => contracts::contract_def(context, stmt),
})
.collect();

fe::Module { body: lowered_body }
}
Loading

0 comments on commit 815e4bf

Please sign in to comment.