add circuit type parsing up to type checking

This commit is contained in:
collin 2022-06-15 16:08:05 -07:00
parent 3882ec2425
commit 993b86e8c7
19 changed files with 374 additions and 32 deletions

View File

@ -36,4 +36,3 @@ impl fmt::Display for MemberAccess {
write!(f, "{}.{}", self.inner, self.name)
}
}

View File

@ -38,4 +38,3 @@ impl fmt::Display for StaticAccess {
write!(f, "{}::{}", self.inner, self.name)
}
}

View File

@ -53,4 +53,4 @@ impl fmt::Display for Circuit {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
self.format(f)
}
}
}

View File

@ -60,4 +60,4 @@ impl fmt::Display for CircuitMember {
CircuitMember::CircuitFunction(ref function) => write!(f, "{}", function),
}
}
}
}

View File

@ -18,4 +18,4 @@ pub mod circuit;
pub use circuit::*;
pub mod circuit_member;
pub use circuit_member::*;
pub use circuit_member::*;

View File

@ -45,4 +45,3 @@ impl fmt::Display for AccessExpression {
}
}
}

View File

@ -44,25 +44,26 @@ pub use unary::*;
mod value;
pub use value::*;
/// Expression that evaluates to a value.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub enum Expression {
/// An identifier expression.
Identifier(Identifier),
/// A literal expression.
Value(ValueExpression),
/// A binary expression, e.g., `42 + 24`.
Binary(BinaryExpression),
/// An unary expression.
Unary(UnaryExpression),
/// A ternary conditional expression `cond ? if_expr : else_expr`.
Ternary(TernaryExpression),
/// A call expression, e.g., `my_fun(args)`.
Call(CallExpression),
/// An expression constructing a structure like `Foo { bar: 42, baz }`.
CircuitInit(CircuitInitExpression),
/// An expression of type "error".
/// Will result in a compile error eventually.
Err(ErrExpression),
/// An identifier expression.
Identifier(Identifier),
/// A ternary conditional expression `cond ? if_expr : else_expr`.
Ternary(TernaryExpression),
/// An unary expression.
Unary(UnaryExpression),
/// A literal expression.
Value(ValueExpression),
}
impl Node for Expression {
@ -75,6 +76,7 @@ impl Node for Expression {
Unary(n) => n.span(),
Ternary(n) => n.span(),
Call(n) => n.span(),
CircuitInit(n) => n.span(),
Err(n) => n.span(),
}
}
@ -88,6 +90,7 @@ impl Node for Expression {
Unary(n) => n.set_span(span),
Ternary(n) => n.set_span(span),
Call(n) => n.set_span(span),
CircuitInit(n) => n.set_span(span),
Err(n) => n.set_span(span),
}
}
@ -103,6 +106,7 @@ impl fmt::Display for Expression {
Unary(n) => n.fmt(f),
Ternary(n) => n.fmt(f),
Call(n) => n.fmt(f),
CircuitInit(n) => n.fmt(f),
Err(n) => n.fmt(f),
}
}

View File

@ -46,6 +46,7 @@ impl<R: ReconstructingReducer> ReconstructingDirector<R> {
Expression::Unary(unary) => Expression::Unary(self.reduce_unary(unary)?),
Expression::Ternary(ternary) => Expression::Ternary(self.reduce_ternary(ternary)?),
Expression::Call(call) => Expression::Call(self.reduce_call(call)?),
Expression::CircuitInit(circuit_init) => Expression::CircuitInit(self.reduce_circuit_init(circuit_init)?),
Expression::Err(s) => Expression::Err(s.clone()),
};
@ -257,13 +258,19 @@ impl<R: ReconstructingReducer> ReconstructingDirector<R> {
}
let mut functions = IndexMap::new();
let mut circuits = IndexMap::new();
for (name, function) in program.functions.iter() {
functions.insert(*name, self.reduce_function(function)?);
}
for (name, circuit) in program.circuits.iter() {
circuits.insert(*name, self.reduce_circuit(circuit)?);
}
self.reducer.reduce_program(program, inputs, functions)
self.reducer.reduce_program(program, inputs, functions, circuits)
}
// Functions
pub fn reduce_function_input_variable(
&mut self,
variable: &FunctionInputVariable,
@ -299,4 +306,62 @@ impl<R: ReconstructingReducer> ReconstructingDirector<R> {
self.reducer
.reduce_function(function, identifier, inputs, output, block)
}
// Circuits
pub fn reduce_circuit_variable_initializer(
&mut self,
variable: &CircuitVariableInitializer,
) -> Result<CircuitVariableInitializer> {
let identifier = self.reduce_identifier(&variable.identifier)?;
let expression = variable
.expression
.as_ref()
.map(|expr| self.reduce_expression(expr))
.transpose()?;
self.reducer
.reduce_circuit_variable_initializer(variable, identifier, expression)
}
pub fn reduce_circuit_init(&mut self, circuit_init: &CircuitInitExpression) -> Result<CircuitInitExpression> {
let name = self.reduce_identifier(&circuit_init.name)?;
let mut members = vec![];
for member in circuit_init.members.iter() {
members.push(self.reduce_circuit_variable_initializer(member)?);
}
self.reducer.reduce_circuit_init(circuit_init, name, members)
}
pub fn reduce_circuit_member(&mut self, circuit_member: &CircuitMember) -> Result<CircuitMember> {
let new = match circuit_member {
CircuitMember::CircuitConst(identifier, type_, value) => CircuitMember::CircuitConst(
self.reduce_identifier(identifier)?,
self.reduce_type(type_, &identifier.span)?,
self.reduce_expression(value)?,
),
CircuitMember::CircuitVariable(identifier, type_) => CircuitMember::CircuitVariable(
self.reduce_identifier(identifier)?,
self.reduce_type(type_, &identifier.span)?,
),
CircuitMember::CircuitFunction(function) => {
CircuitMember::CircuitFunction(Box::new(self.reduce_function(function)?))
}
};
self.reducer.reduce_circuit_member(circuit_member, new)
}
pub fn reduce_circuit(&mut self, circuit: &Circuit) -> Result<Circuit> {
let circuit_name = self.reduce_identifier(&circuit.circuit_name)?;
let mut members = vec![];
for member in circuit.members.iter() {
members.push(self.reduce_circuit_member(member)?);
}
self.reducer.reduce_circuit(circuit, circuit_name, members)
}
}

View File

@ -246,11 +246,13 @@ pub trait ReconstructingReducer {
program: &Program,
expected_input: Vec<FunctionInput>,
functions: IndexMap<Identifier, Function>,
circuits: IndexMap<Identifier, Circuit>,
) -> Result<Program> {
Ok(Program {
name: program.name.clone(),
expected_input,
functions,
circuits,
})
}
@ -294,4 +296,39 @@ pub trait ReconstructingReducer {
span: function.span,
})
}
fn reduce_circuit_variable_initializer(
&mut self,
_variable: &CircuitVariableInitializer,
identifier: Identifier,
expression: Option<Expression>,
) -> Result<CircuitVariableInitializer> {
Ok(CircuitVariableInitializer { identifier, expression })
}
fn reduce_circuit_init(
&mut self,
circuit_init: &CircuitInitExpression,
name: Identifier,
members: Vec<CircuitVariableInitializer>,
) -> Result<CircuitInitExpression> {
Ok(CircuitInitExpression {
name,
members,
span: circuit_init.span.clone(),
})
}
fn reduce_circuit_member(&mut self, _circuit_member: &CircuitMember, new: CircuitMember) -> Result<CircuitMember> {
Ok(new)
}
fn reduce_circuit(
&mut self,
_circuit: &Circuit,
circuit_name: Identifier,
members: Vec<CircuitMember>,
) -> Result<Circuit> {
Ok(Circuit { circuit_name, members })
}
}

View File

@ -61,6 +61,10 @@ pub trait ExpressionVisitor<'a> {
fn visit_err(&mut self, _input: &'a ErrExpression) -> VisitResult {
Default::default()
}
fn visit_circuit_init(&mut self, _input: &'a CircuitInitExpression) -> VisitResult {
Default::default()
}
}
pub trait StatementVisitor<'a> {
@ -105,4 +109,8 @@ pub trait ProgramVisitor<'a> {
fn visit_function(&mut self, _input: &'a Function) -> VisitResult {
Default::default()
}
fn visit_circuit(&mut self, _input: &'a Circuit) -> VisitResult {
Default::default()
}
}

View File

@ -41,6 +41,7 @@ pub trait ExpressionVisitorDirector<'a>: VisitorDirector<'a> {
Expression::Unary(expr) => self.visit_unary(expr, additional),
Expression::Ternary(expr) => self.visit_ternary(expr, additional),
Expression::Call(expr) => self.visit_call(expr, additional),
Expression::CircuitInit(expr) => self.visit_circuit_init(expr, additional),
Expression::Err(expr) => self.visit_err(expr, additional),
};
}
@ -99,6 +100,21 @@ pub trait ExpressionVisitorDirector<'a>: VisitorDirector<'a> {
None
}
fn visit_circuit_init(
&mut self,
input: &'a CircuitInitExpression,
additional: &Self::AdditionalInput,
) -> Option<Self::Output> {
if let VisitResult::VisitChildren = self.visitor_ref().visit_circuit_init(input) {
input.members.iter().for_each(|member| {
if let Some(expr) = &member.expression {
self.visit_expression(expr, additional);
}
});
}
None
}
fn visit_err(&mut self, input: &'a ErrExpression, _additional: &Self::AdditionalInput) -> Option<Self::Output> {
self.visitor_ref().visit_err(input);
None
@ -184,6 +200,7 @@ pub trait ProgramVisitorDirector<'a>: VisitorDirector<'a> + StatementVisitorDire
.functions
.values()
.for_each(|function| self.visit_function(function));
input.circuits.values().for_each(|circuit| self.visit_circuit(circuit));
}
}
@ -192,4 +209,20 @@ pub trait ProgramVisitorDirector<'a>: VisitorDirector<'a> + StatementVisitorDire
self.visit_block(&input.block);
}
}
fn visit_circuit(&mut self, input: &'a Circuit) {
if let VisitResult::VisitChildren = self.visitor_ref().visit_circuit(input) {
input.members.iter().for_each(|member| {
match member {
CircuitMember::CircuitConst(_, _, expr) => {
self.visit_expression(expr, &Default::default());
}
CircuitMember::CircuitFunction(func) => {
self.visit_function(func);
}
CircuitMember::CircuitVariable(_, _) => {}
};
})
}
}
}

View File

@ -17,7 +17,7 @@
//! A Leo program consists of import, circuit, and function definitions.
//! Each defined type consists of ast statements and expressions.
use crate::{Function, FunctionInput, Identifier};
use crate::{Circuit, Function, FunctionInput, Identifier};
use indexmap::IndexMap;
use serde::{Deserialize, Serialize};
@ -32,8 +32,10 @@ pub struct Program {
/// Expected main function inputs.
/// Empty after parsing.
pub expected_input: Vec<FunctionInput>,
/// A map from function names to their definitions.
/// A map from function names to function definitions.
pub functions: IndexMap<Identifier, Function>,
/// A map from circuit names to circuit definitions.
pub circuits: IndexMap<Identifier, Circuit>,
}
impl AsRef<Program> for Program {
@ -59,6 +61,7 @@ impl Program {
name,
expected_input: vec![],
functions: IndexMap::new(),
circuits: IndexMap::new(),
}
}

View File

@ -92,8 +92,8 @@ impl<'a> ParserContext<'a> {
&self.token.token == tok
}
/// Removes the next token if it exists and returns it, or [None] if
/// the next token does not exist.
/// Returns `true` if the next token is equal to the given token.
/// Advances the parser to the next token.
pub(super) fn eat(&mut self, token: &Token) -> bool {
self.check(token).then(|| self.bump()).is_some()
}

View File

@ -17,6 +17,7 @@
use super::*;
use leo_errors::{ParserError, Result};
use leo_span::sym;
const INT_TYPES: &[Token] = &[
Token::I8,
@ -374,6 +375,29 @@ impl ParserContext<'_> {
Some(Ok(gt))
}
/// Returns an [`Expression`] AST node if the next tokens represent a
/// circuit initialization expression.
pub fn parse_circuit_expression(&mut self, identifier: Identifier) -> Result<Expression> {
let (members, _, span) = self.parse_list(Delimiter::Brace, Some(Token::Comma), |p| {
let expression = if p.eat(&Token::Colon) {
// Parse individual circuit variable declarations.
Some(p.parse_expression()?)
} else {
None
};
Ok(Some(CircuitVariableInitializer {
identifier: p.expect_ident()?,
expression,
}))
})?;
Ok(Expression::CircuitInit(CircuitInitExpression {
span: &identifier.span + &span,
name: identifier,
members,
}))
}
/// Returns an [`Expression`] AST node if the next token is a primary expression:
/// - Literals: field, group, unsigned integer, signed integer, boolean, address
/// - Aggregate types: array, tuple
@ -425,7 +449,22 @@ impl ParserContext<'_> {
Token::StaticString(value) => Expression::Value(ValueExpression::String(value, span)),
Token::Ident(name) => {
let ident = Identifier { name, span };
Expression::Identifier(ident)
if !self.disallow_circuit_construction && self.eat(&Token::LeftCurly) {
self.parse_circuit_expression(ident)?
} else {
Expression::Identifier(ident)
}
}
Token::SelfUpper => {
let ident = Identifier {
name: sym::SelfUpper,
span,
};
if !self.disallow_circuit_construction && self.eat(&Token::LeftCurly) {
self.parse_circuit_expression(ident)?
} else {
Expression::Identifier(ident)
}
}
t if crate::type_::TYPE_TOKENS.contains(&t) => Expression::Identifier(Identifier {
name: t.keyword_to_symbol().unwrap(),

View File

@ -23,19 +23,25 @@ impl ParserContext<'_> {
/// Returns a [`Program`] AST if all tokens can be consumed and represent a valid Leo program.
pub fn parse_program(&mut self) -> Result<Program> {
let mut functions = IndexMap::new();
let mut circuits = IndexMap::new();
while self.has_next() {
match &self.token.token {
Token::Circuit => {
let (id, circuit) = self.parse_circuit()?;
circuits.insert(id, circuit);
}
Token::Const if self.peek_is_function() => {
let (id, function) = self.parse_function()?;
functions.insert(id, function);
}
Token::Ident(sym::test) => return Err(ParserError::test_function(self.token.span).into()),
// Const functions share the first token with the global Const.
Token::Const if self.peek_is_function() => {
let (id, function) = self.parse_function_declaration()?;
functions.insert(id, function);
}
Token::Function => {
let (id, function) = self.parse_function_declaration()?;
let (id, function) = self.parse_function()?;
functions.insert(id, function);
}
_ => return Err(Self::unexpected_item(&self.token).into()),
}
}
@ -43,13 +49,16 @@ impl ParserContext<'_> {
name: String::new(),
expected_input: Vec::new(),
functions,
circuits,
})
}
fn unexpected_item(token: &SpannedToken) -> ParserError {
ParserError::unexpected(
&token.token,
[Token::Function, Token::Ident(sym::test)]
[Token::Function,
Token::Circuit,
Token::Ident(sym::test)]
.iter()
.map(|x| format!("'{}'", x))
.collect::<Vec<_>>()
@ -58,6 +67,121 @@ impl ParserContext<'_> {
)
}
/// Returns a [`CircuitMember`] AST node if the next tokens represent a circuit member variable
/// or circuit member function or circuit member constant.
pub fn parse_circuit_declaration(&mut self) -> Result<Vec<CircuitMember>> {
let mut members = Vec::new();
let (mut semi_colons, mut commas) = (false, false);
while self.eat(&Token::RightCurly) {
members.push(if self.peek_is_function() {
// function
self.parse_member_function_declaration()?
} else if self.eat(&Token::Static) {
// static const
self.parse_const_member_variable_declaration()?
} else {
// variable
let variable = self.parse_member_variable_declaration()?;
if self.eat(&Token::Semicolon) {
if commas {
self.emit_err(ParserError::mixed_commas_and_semicolons(self.token.span));
}
semi_colons = true;
}
if self.eat(&Token::Comma) {
if semi_colons {
self.emit_err(ParserError::mixed_commas_and_semicolons(self.token.span));
}
commas = true;
}
variable
});
}
self.ban_mixed_member_order(&members);
Ok(members)
}
/// Emits errors if order isn't `consts variables functions`.
fn ban_mixed_member_order(&self, members: &[CircuitMember]) {
let mut had_var = false;
let mut had_fun = false;
for member in members {
match member {
CircuitMember::CircuitConst(id, _, e) if had_var => {
self.emit_err(ParserError::member_const_after_var(id.span() + e.span()));
}
CircuitMember::CircuitConst(id, _, e) if had_fun => {
self.emit_err(ParserError::member_const_after_fun(id.span() + e.span()));
}
CircuitMember::CircuitVariable(id, _) if had_fun => {
self.emit_err(ParserError::member_var_after_fun(id.span()));
}
CircuitMember::CircuitConst(..) => {}
CircuitMember::CircuitVariable(..) => had_var = true,
CircuitMember::CircuitFunction(..) => had_fun = true,
}
}
}
/// Parses `IDENT: TYPE`.
fn parse_typed_field_name(&mut self) -> Result<(Identifier, Type)> {
let name = self.expect_ident()?;
self.expect(&Token::Colon)?;
let type_ = self.parse_all_types()?.0;
Ok((name, type_))
}
/// Returns a [`CircuitMember`] AST node if the next tokens represent a circuit member static constant.
pub fn parse_const_member_variable_declaration(&mut self) -> Result<CircuitMember> {
self.expect(&Token::Static)?;
self.expect(&Token::Const)?;
// `IDENT: TYPE = EXPR`:
let (name, type_) = self.parse_typed_field_name()?;
self.expect(&Token::Assign)?;
let expr = self.parse_expression()?;
self.expect(&Token::Semicolon)?;
Ok(CircuitMember::CircuitConst(name, type_, expr))
}
/// Returns a [`CircuitMember`] AST node if the next tokens represent a circuit member variable.
pub fn parse_member_variable_declaration(&mut self) -> Result<CircuitMember> {
let (name, type_) = self.parse_typed_field_name()?;
Ok(CircuitMember::CircuitVariable(name, type_))
}
/// Returns a [`CircuitMember`] AST node if the next tokens represent a circuit member function.
pub fn parse_member_function_declaration(&mut self) -> Result<CircuitMember> {
if self.peek_is_function() {
let function = self.parse_function()?;
Ok(CircuitMember::CircuitFunction(Box::new(function.1)))
} else {
return Err(Self::unexpected_item(&self.token).into());
}
}
/// Returns an [`(Identifier, Function)`] ast node if the next tokens represent a circuit declaration.
pub(super) fn parse_circuit(&mut self) -> Result<(Identifier, Circuit)> {
self.expect(&Token::Circuit)?;
let circuit_name = self.expect_ident()?;
self.expect(&Token::LeftCurly)?;
let members = self.parse_circuit_declaration()?;
Ok((circuit_name.clone(), Circuit { circuit_name, members }))
}
/// Returns a [`ParamMode`] AST node if the next tokens represent a function parameter mode.
pub(super) fn parse_function_parameter_mode(&mut self) -> Result<ParamMode> {
let public = self.eat(&Token::Public).then(|| self.prev_token.span);
@ -106,7 +230,7 @@ impl ParserContext<'_> {
/// Returns an [`(Identifier, Function)`] AST node if the next tokens represent a function name
/// and function definition.
fn parse_function_declaration(&mut self) -> Result<(Identifier, Function)> {
fn parse_function(&mut self) -> Result<(Identifier, Function)> {
// Parse `function IDENT`.
let start = self.expect(&Token::Function)?;
let name = self.expect_ident()?;

View File

@ -87,8 +87,10 @@ pub enum Token {
U32,
U64,
U128,
SelfUpper,
// Regular Keywords
Circuit,
Console,
/// Const variable and a const function.
Const,
@ -103,6 +105,8 @@ pub enum Token {
/// For public inputs.
Public,
Return,
SelfLower,
Static,
// Meta Tokens
Eof,
@ -115,6 +119,7 @@ pub enum Token {
pub const KEYWORD_TOKENS: &[Token] = &[
Token::Address,
Token::Bool,
Token::Circuit,
Token::Console,
Token::Const,
Token::Constant,
@ -134,7 +139,10 @@ pub const KEYWORD_TOKENS: &[Token] = &[
Token::Let,
Token::Public,
Token::Return,
Token::SelfLower,
Token::SelfUpper,
Token::Scalar,
Token::Static,
Token::String,
Token::True,
Token::U8,
@ -155,6 +163,7 @@ impl Token {
Some(match self {
Token::Address => sym::address,
Token::Bool => sym::bool,
Token::Circuit => sym::circuit,
Token::Console => sym::console,
Token::Const => sym::Const,
Token::Constant => sym::Constant,
@ -175,6 +184,9 @@ impl Token {
Token::Public => sym::Public,
Token::Return => sym::Return,
Token::Scalar => sym::scalar,
Token::SelfLower => sym::SelfLower,
Token::SelfUpper => sym::SelfUpper,
Token::Static => sym::Static,
Token::String => sym::string,
Token::True => sym::True,
Token::U8 => sym::u8,
@ -251,7 +263,9 @@ impl fmt::Display for Token {
U32 => write!(f, "u32"),
U64 => write!(f, "u64"),
U128 => write!(f, "u128"),
SelfUpper => write!(f, "Self"),
Circuit => write!(f, "circuit"),
Console => write!(f, "console"),
Const => write!(f, "const"),
Constant => write!(f, "constant"),
@ -261,8 +275,10 @@ impl fmt::Display for Token {
If => write!(f, "if"),
In => write!(f, "in"),
Let => write!(f, "let"),
SelfLower => write!(f, "self"),
Public => write!(f, "public"),
Return => write!(f, "return"),
Static => write!(f, "static"),
Eof => write!(f, "<eof>"),
}
}

View File

@ -48,13 +48,14 @@ impl<'a> ExpressionVisitorDirector<'a> for Director<'a> {
fn visit_expression(&mut self, input: &'a Expression, expected: &Self::AdditionalInput) -> Option<Self::Output> {
if let VisitResult::VisitChildren = self.visitor.visit_expression(input) {
return match input {
Expression::Identifier(expr) => self.visit_identifier(expr, expected),
Expression::Value(expr) => self.visit_value(expr, expected),
Expression::Binary(expr) => self.visit_binary(expr, expected),
Expression::Unary(expr) => self.visit_unary(expr, expected),
Expression::Ternary(expr) => self.visit_ternary(expr, expected),
Expression::Call(expr) => self.visit_call(expr, expected),
Expression::CircuitInit(expr) => self.visit_circuit_init(expr, expected),
Expression::Err(expr) => self.visit_err(expr, expected),
Expression::Identifier(expr) => self.visit_identifier(expr, expected),
Expression::Ternary(expr) => self.visit_ternary(expr, expected),
Expression::Unary(expr) => self.visit_unary(expr, expected),
Expression::Value(expr) => self.visit_value(expr, expected),
};
}
@ -535,4 +536,8 @@ impl<'a> ExpressionVisitorDirector<'a> for Director<'a> {
expr => self.visit_expression(expr, expected),
}
}
fn visit_circuit_init(&mut self, input: &'a CircuitInitExpression, additional: &Self::AdditionalInput) -> Option<Self::Output> {
todo!()
}
}

View File

@ -53,4 +53,11 @@ impl<'a> ProgramVisitorDirector<'a> for Director<'a> {
}
}
}
fn visit_circuit(&mut self, input: &'a Circuit) {
todo!()
// if let VisitResult::VisitChildren = self.visitor_ref().visit_function(input) {
// self.
// }
}
}

View File

@ -180,6 +180,7 @@ symbols! {
// general keywords
AlwaysConst,
assert,
circuit,
Class: "class",
context,
CoreFunction,
@ -198,6 +199,9 @@ symbols! {
prelude,
Public,
Return: "return",
SelfLower: "self",
SelfUpper: "Self",
Static: "static",
Star: "*",
std,
Struct: "struct",