1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119

use crate::language::InternSymbol;
use crate::parser::primary::{Primary, AccessItem, Atom};
use crate::parser::operator::BinaryOp;
use crate::parser::expr::{Expr, ExprMeta};


// LValues

#[derive(Debug, Clone)]
pub enum LValue {
    Identifier(InternSymbol),
    Attribute(Box<AttributeTarget>), // receiver, attribute name
    Index(Box<IndexTarget>), // receiver, index expression
    Tuple(Box<[LValue]>),
    // Unpack(Box<LValue>),
}

#[derive(Debug, Clone)]
pub struct AttributeTarget {
    pub receiver: Primary,
    pub name: InternSymbol,
}

#[derive(Debug, Clone)]
pub struct IndexTarget {
    pub receiver: Primary,
    pub index: ExprMeta,
}

// Assignments

#[derive(Debug, Clone)]
pub struct Assignment {
    pub lhs: LValue,
    pub op: Option<BinaryOp>, // e.g. for +=, -=, *=, ...
    pub rhs: Expr,
    pub nonlocal: bool,
}

// Declarations

// TODO rename
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum DeclType {
    Immutable,
    Mutable,
}

#[derive(Debug, Clone)]
pub struct Declaration {
    pub decl: DeclType,
    pub lhs: LValue,
    pub init: Expr,
}

// Convert expressions to LValues...

/*
    lvalue ::= identifier | primary index-access | primary member-access ;

    lvalue-expression ::= lvalue | lvalue-list | "(" lvalue ")" ;   (* basically just lvalues, and tuples of lvalues *)
    lvalue-list ::= lvalue-expression ( "," lvalue-expression )* ;
*/

impl TryFrom<Atom> for LValue {
    type Error = ();
    fn try_from(atom: Atom) -> Result<Self, Self::Error> {
        match atom {
            Atom::Identifier(name) => Ok(LValue::Identifier(name)),
            Atom::Group(expr) => (*expr).try_into(),
            _ => Err(())
        }
    }
}

impl TryFrom<Primary> for LValue {
    type Error = ();
    fn try_from(primary: Primary) -> Result<Self, Self::Error> {
        // remove the last item so that primary will eval to the reciever
        let (atom, mut path) = primary.take();
        let tail = path.pop();
        let receiver = Primary::new(atom, path);
        
        let lvalue = match tail {
            Some(AccessItem::Attribute(name)) 
                => LValue::Attribute(Box::new(AttributeTarget { receiver, name })),
            Some(AccessItem::Index(index)) 
                => LValue::Index(Box::new(IndexTarget { receiver, index })),
            _ => return Err(()),
        };
        
        Ok(lvalue)
    }
}

impl TryFrom<Expr> for LValue {
    type Error = ();
    fn try_from(expr: Expr) -> Result<Self, Self::Error> {
        match expr {
            Expr::Atom(atom) => atom.try_into(),
            
            Expr::Primary(primary) => primary.try_into(),
            
            Expr::Tuple(expr_list) => {
                let mut lvalue_list = Vec::<LValue>::new();
                
                for expr in expr_list.into_vec().into_iter() {
                    let lvalue = expr.take_variant().try_into()?;
                    lvalue_list.push(lvalue);
                }
                
                Ok(Self::Tuple(lvalue_list.into_boxed_slice()))
            },
            
            _ => Err(()),
        }
    }
}