DEV: AST enum for a shape.

This commit is contained in:
Jeremy Wall 2020-04-26 12:16:20 -04:00
parent 2b835e5886
commit 3d34355fe7
3 changed files with 246 additions and 14 deletions

View File

@ -19,7 +19,7 @@ use std::cmp::Eq;
use std::cmp::Ordering;
use std::cmp::PartialEq;
use std::cmp::PartialOrd;
use std::convert::Into;
use std::convert::{Into, TryFrom, TryInto};
use std::fmt;
use std::hash::Hash;
use std::hash::Hasher;
@ -30,6 +30,7 @@ use abortable_parser;
use crate::build::scope::Scope;
use crate::build::Val;
use crate::error::BuildError;
pub mod printer;
pub mod walk;
@ -218,21 +219,58 @@ macro_rules! make_expr {
/// This is usually used as the body of a tuple in the UCG AST.
pub type FieldList = Vec<(Token, Expression)>; // Token is expected to be a symbol
/// Represents a Value in the UCG parsed AST.
#[derive(Debug, PartialEq, Clone)]
pub enum Value {
// Constant Values
Empty(Position),
Boolean(PositionedItem<bool>),
Int(PositionedItem<i64>),
Float(PositionedItem<f64>),
Str(PositionedItem<String>),
Symbol(PositionedItem<String>),
// Complex Values
Tuple(PositionedItem<FieldList>),
List(ListDef),
pub type ShapeTuple = Vec<(Token, Shape)>;
pub type ShapeList = Vec<Shape>;
#[derive(PartialEq, Debug, Clone)]
pub struct FuncShapeDef {
args: Vec<Shape>,
ret: Box<Shape>,
}
#[derive(PartialEq, Debug, Clone)]
pub struct ModuleShapeDef {
items: ShapeTuple,
ret: Box<Shape>,
}
macro_rules! value_enum {
($doc:meta $i:tt, $t:ty, $l:ty, $($extra:tt)*) => {
#[$doc]
#[derive(PartialEq, Debug, Clone)]
pub enum $i {
// Simple Values
Empty(Position),
Boolean(PositionedItem<bool>),
Int(PositionedItem<i64>),
Float(PositionedItem<f64>),
Str(PositionedItem<String>),
Symbol(PositionedItem<String>),
// Complex Values
Tuple(PositionedItem<$t>),
List($l),
// Extra items
$( $extra )*
}
}
}
value_enum!(
doc="Value types represent the Values that UCG can have."
Value,
FieldList,
ListDef,
);
value_enum!(
doc="Shapes represent the types that UCG values or expressions can have."
Shape,
ShapeTuple,
PositionedItem<ShapeList>,
Func(FuncShapeDef),
Module(ModuleShapeDef),
);
impl Value {
/// Returns the type name of the Value it is called on as a string.
pub fn type_name(&self) -> String {
@ -307,6 +345,43 @@ impl Value {
&Value::List(_)
)
}
fn derive_shape(&self) -> Result<Shape, BuildError> {
let shape = match self {
Value::Empty(p) => Shape::Empty(p.clone()),
Value::Boolean(p) => Shape::Boolean(p.clone()),
Value::Int(p) => Shape::Int(p.clone()),
Value::Float(p) => Shape::Float(p.clone()),
Value::Str(p) => Shape::Str(p.clone()),
// Symbols in a shape are placeholder. They allow a form of genericity
// in the shape. They can be any type and are only refined down.
// by their presence in an expression.
Value::Symbol(p) => Shape::Symbol(p.clone()),
Value::Tuple(flds) => {
let mut field_shapes = Vec::new();
for &(ref tok, ref expr) in &flds.val {
field_shapes.push((tok.clone(), expr.try_into()?));
}
Shape::Tuple(PositionedItem::new(field_shapes, flds.pos.clone()))
}
Value::List(flds) => {
let mut field_shapes = Vec::new();
for f in &flds.elems {
field_shapes.push(f.try_into()?);
}
Shape::List(PositionedItem::new(field_shapes, flds.pos.clone()))
}
};
Ok(shape)
}
}
impl TryFrom<&Value> for Shape {
type Error = crate::error::BuildError;
fn try_from(v: &Value) -> Result<Self, Self::Error> {
v.derive_shape()
}
}
/// Represents an expansion of a Macro that is expected to already have been
@ -769,6 +844,28 @@ impl Expression {
&Expression::Debug(ref def) => &def.pos,
}
}
fn derive_shape(&self) -> Result<Shape, BuildError> {
// FIXME(jwall): Implement this
let shape = match self {
Expression::Simple(ref v) => v.try_into()?,
Expression::Format(def) => {
Shape::Str(PositionedItem::new("".to_owned(), def.pos.clone()))
}
Expression::Not(def) => Shape::Boolean(PositionedItem::new(true, def.pos.clone())),
Expression::Grouped(v, _pos) => v.as_ref().try_into()?,
_ => Shape::Empty(Position::new(0, 0, 0)),
};
Ok(shape)
}
}
impl TryFrom<&Expression> for Shape {
type Error = crate::error::BuildError;
fn try_from(e: &Expression) -> Result<Self, Self::Error> {
e.derive_shape()
}
}
impl fmt::Display for Expression {
@ -868,3 +965,6 @@ impl Statement {
}
}
}
#[cfg(test)]
mod test;

128
src/ast/test.rs Normal file
View File

@ -0,0 +1,128 @@
// Copyright 2020 Jeremy Wall
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use crate::ast::{
Expression, FormatArgs, FormatDef, ListDef, NotDef, Position, PositionedItem, Shape, Token,
TokenType, Value,
};
#[test]
fn derive_shape_values() {
let value_cases = vec![
(
Value::Empty(Position::new(0, 0, 0)),
Shape::Empty(Position::new(0, 0, 0)),
),
(
Value::Boolean(PositionedItem::new(false, Position::new(0, 1, 2))),
Shape::Boolean(PositionedItem::new(false, Position::new(0, 1, 2))),
),
(
Value::Boolean(PositionedItem::new(true, Position::new(0, 1, 2))),
Shape::Boolean(PositionedItem::new(true, Position::new(0, 1, 2))),
),
(
Value::Int(PositionedItem::new(1, Position::new(0, 1, 2))),
Shape::Int(PositionedItem::new(1, Position::new(0, 1, 2))),
),
(
Value::Float(PositionedItem::new(2.0, Position::new(0, 1, 2))),
Shape::Float(PositionedItem::new(2.0, Position::new(0, 1, 2))),
),
(
Value::Str(PositionedItem::new(
"foo".to_owned(),
Position::new(0, 1, 2),
)),
Shape::Str(PositionedItem::new(
"foo".to_owned(),
Position::new(0, 1, 2),
)),
),
(
Value::Tuple(PositionedItem::new(
vec![(
Token::new("foo", TokenType::BAREWORD, Position::new(0, 0, 0)),
Expression::Simple(Value::Int(PositionedItem::new(3, Position::new(0, 0, 0)))),
)],
Position::new(0, 0, 0),
)),
Shape::Tuple(PositionedItem::new(
vec![(
Token::new("foo", TokenType::BAREWORD, Position::new(0, 0, 0)),
Shape::Int(PositionedItem::new(3, Position::new(0, 0, 0))),
)],
Position::new(0, 0, 0),
)),
),
(
Value::List(ListDef {
elems: vec![Expression::Simple(Value::Int(PositionedItem::new(
3,
Position::new(0, 0, 0),
)))],
pos: Position::new(0, 0, 0),
}),
Shape::List(PositionedItem::new(
vec![Shape::Int(PositionedItem::new(3, Position::new(0, 0, 0)))],
Position::new(0, 0, 0),
)),
),
];
for (val, shape) in value_cases {
assert_eq!(val.derive_shape().unwrap(), shape);
}
}
#[test]
fn derive_shape_expressions() {
let expr_cases = vec![
(
Expression::Simple(Value::Int(PositionedItem::new(3, Position::new(0, 0, 0)))),
Shape::Int(PositionedItem::new(3, Position::new(0, 0, 0))),
),
(
Expression::Grouped(
Box::new(Expression::Simple(Value::Int(PositionedItem::new(
3,
Position::new(0, 0, 0),
)))),
Position::new(0, 0, 0),
),
Shape::Int(PositionedItem::new(3, Position::new(0, 0, 0))),
),
(
Expression::Format(FormatDef {
template: "".to_owned(),
args: FormatArgs::List(Vec::new()),
pos: Position::new(0, 0, 0),
}),
Shape::Str(PositionedItem::new("".to_owned(), Position::new(0, 0, 0))),
),
(
Expression::Not(NotDef {
expr: Box::new(Expression::Simple(Value::Boolean(PositionedItem::new(
true,
Position::new(0, 0, 0),
)))),
pos: Position::new(1, 0, 0),
}),
Shape::Boolean(PositionedItem::new(true, Position::new(1, 0, 0))),
),
];
for (expr, shape) in expr_cases {
assert_eq!(expr.derive_shape().unwrap(), shape);
}
}

View File

@ -402,6 +402,8 @@ impl AST {
Expression::Func(def) => {
ops.push(Op::InitList, def.pos.clone());
for b in def.argdefs {
// FIXME(jwall): if there is a projection
// then add the projection check here?
ops.push(Op::Sym(b.val), b.pos.clone());
ops.push(Op::Element, b.pos);
}
@ -460,6 +462,8 @@ impl AST {
// Init our module tuple bindings
ops.push(Op::InitTuple, def.pos.clone());
for (t, e) in argset {
// FIXME(jwall): if there is a projection
// then add the projection check here?
ops.push(Op::Sym(t.fragment), t.pos.clone());
Self::translate_expr(e, &mut ops, root);
ops.push(Op::Field, t.pos);