tycho_asm/asm/
mod.rs

1mod opcodes;
2mod util;
3
4use chumsky::span::Span;
5use tycho_types::prelude::*;
6
7use self::opcodes::{Context, Scope, cp0};
8use crate::ast;
9
10pub fn assemble(ast: &[ast::Stmt], span: ast::Span) -> Result<Cell, AsmError> {
11    let opcodes = cp0();
12
13    let mut context = Context::new();
14    let mut scope = Scope::new();
15    for stmt in ast {
16        context.add_stmt(opcodes, stmt, &mut scope)?;
17    }
18
19    context
20        .into_builder(span)?
21        .build()
22        .map_err(|e| AsmError::StoreError { inner: e, span })
23}
24
25pub fn check(ast: &[ast::Stmt], span: ast::Span) -> Vec<AsmError> {
26    let opcodes = cp0();
27
28    let mut errors = Vec::new();
29    let mut context = Context::new();
30    let mut scope = Scope::new();
31    context.set_allow_invalid();
32    for stmt in ast {
33        if let Err(e) = context.add_stmt(opcodes, stmt, &mut scope) {
34            errors.push(e);
35        }
36    }
37
38    if let Err(e) = context.into_builder(span).and_then(|b| {
39        b.build()
40            .map_err(|e| AsmError::StoreError { inner: e, span })
41    }) {
42        errors.push(e);
43    }
44
45    errors
46}
47
48impl ast::InstrArgValue<'_> {
49    fn ty(&self) -> ArgType {
50        match self {
51            ast::InstrArgValue::Nat(_) => ArgType::Nat,
52            &ast::InstrArgValue::MethodId(_) => ArgType::MethodId,
53            ast::InstrArgValue::SReg(_) => ArgType::StackRegister,
54            ast::InstrArgValue::CReg(_) => ArgType::ControlRegister,
55            ast::InstrArgValue::Slice(_) => ArgType::Slice,
56            ast::InstrArgValue::Lib(_) => ArgType::Library,
57            ast::InstrArgValue::Cell(_) => ArgType::Cell,
58            ast::InstrArgValue::Block(_) => ArgType::Block,
59            ast::InstrArgValue::JumpTable(_) => ArgType::JumpTable,
60            ast::InstrArgValue::Use(_) => ArgType::Undefined,
61            ast::InstrArgValue::Invalid => ArgType::Invalid,
62        }
63    }
64}
65
66#[derive(Debug, Copy, Clone)]
67pub enum ArgType {
68    Nat,
69    MethodId,
70    StackRegister,
71    ControlRegister,
72    Slice,
73    Library,
74    Cell,
75    Block,
76    JumpTable,
77    Undefined,
78    Invalid,
79}
80
81impl ArgType {
82    pub fn expected_exact(self) -> ExpectedArgType {
83        ExpectedArgType::Exact(self)
84    }
85
86    pub fn expected_or<T: Into<ExpectedArgType>>(self, other: T) -> ExpectedArgType {
87        ExpectedArgType::OneOf(self, Box::new(other.into()))
88    }
89}
90
91impl std::fmt::Display for ArgType {
92    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
93        f.write_str(match self {
94            Self::Nat => "number",
95            Self::MethodId => "method id",
96            Self::StackRegister => "stack register",
97            Self::ControlRegister => "control register",
98            Self::Slice => "cell slice",
99            Self::Cell => "cell",
100            Self::Library => "library hash",
101            Self::Block => "instruction block",
102            Self::JumpTable => "jump table",
103            Self::Undefined => "undefined",
104            Self::Invalid => "invalid",
105        })
106    }
107}
108
109impl From<ArgType> for ExpectedArgType {
110    #[inline]
111    fn from(value: ArgType) -> Self {
112        Self::Exact(value)
113    }
114}
115
116#[derive(Debug)]
117pub enum ExpectedArgType {
118    Exact(ArgType),
119    OneOf(ArgType, Box<Self>),
120}
121
122impl ExpectedArgType {
123    pub fn join<T: Into<ExpectedArgType>>(mut self, other: T) -> Self {
124        fn join_inner(this: &mut ExpectedArgType, other: ExpectedArgType) {
125            let mut value = std::mem::replace(this, ExpectedArgType::Exact(ArgType::Invalid));
126            match &mut value {
127                ExpectedArgType::Exact(exact) => {
128                    *this = ExpectedArgType::OneOf(*exact, Box::new(other))
129                }
130                ExpectedArgType::OneOf(_, rest) => {
131                    join_inner(rest, other);
132                    *this = value
133                }
134            }
135        }
136
137        join_inner(&mut self, other.into());
138        self
139    }
140}
141
142impl std::fmt::Display for ExpectedArgType {
143    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
144        match self {
145            Self::Exact(arg_type) => std::fmt::Display::fmt(arg_type, f),
146            Self::OneOf(a, rest) => {
147                std::fmt::Display::fmt(a, f)?;
148                let mut rest = rest.as_ref();
149                loop {
150                    match rest {
151                        Self::Exact(b) => return write!(f, " or {b}"),
152                        Self::OneOf(b, next) => {
153                            write!(f, ", {b}")?;
154                            rest = next;
155                        }
156                    }
157                }
158            }
159        }
160    }
161}
162
163#[derive(thiserror::Error, Debug)]
164pub enum AsmError {
165    #[error("undefined variable")]
166    UndefinedVariable(ast::Span),
167    #[error("redefined variable")]
168    RedefinedVariable(ast::Span),
169    #[error("invalid statement")]
170    InvalidStatement(ast::Span),
171    #[error("unknown opcode: {name}")]
172    UnknownOpcode { name: Box<str>, span: ast::Span },
173    #[error("unaligned continuation of {bits} bits")]
174    UnalignedCont { bits: u16, span: ast::Span },
175    #[error("expected {expected}, got {found}")]
176    ArgTypeMismatch {
177        span: ast::Span,
178        found: ArgType,
179        expected: ExpectedArgType,
180    },
181    #[error("invalid register")]
182    InvalidRegister(ast::Span),
183    #[error("invalid jump table key")]
184    InvalidJumpTableKey(ast::Span),
185    #[error("invalid jump table value")]
186    InvalidJumpTableValue(ast::Span),
187    #[error("duplicate jump table entry")]
188    DuplicateJumpTableEntry(ast::Span),
189    #[error("too big integer")]
190    TooBigInteger(ast::Span),
191    #[error("empty jump table")]
192    EmptyJumpTable(ast::Span),
193    #[error("too many args")]
194    TooManyArgs(ast::Span),
195    #[error("not enough args")]
196    NotEnoughArgs(ast::Span),
197    #[error("out of range")]
198    OutOfRange(ast::Span),
199    #[error("invalid usage: {details}")]
200    WrongUsage {
201        details: &'static str,
202        span: ast::Span,
203    },
204    #[error("store error: {inner}")]
205    StoreError {
206        inner: tycho_types::error::Error,
207        span: ast::Span,
208    },
209    #[error("multiple: {0:?}")]
210    Multiple(Box<[AsmError]>),
211}
212
213impl AsmError {
214    pub fn can_ignore(&self) -> bool {
215        matches!(self, Self::ArgTypeMismatch {
216            found: ArgType::Invalid,
217            ..
218        })
219    }
220
221    pub fn span(&self) -> ast::Span {
222        match self {
223            Self::UndefinedVariable(span)
224            | Self::RedefinedVariable(span)
225            | Self::InvalidStatement(span)
226            | Self::UnknownOpcode { span, .. }
227            | Self::UnalignedCont { span, .. }
228            | Self::ArgTypeMismatch { span, .. }
229            | Self::InvalidRegister(span)
230            | Self::InvalidJumpTableKey(span)
231            | Self::InvalidJumpTableValue(span)
232            | Self::DuplicateJumpTableEntry(span)
233            | Self::TooBigInteger(span)
234            | Self::EmptyJumpTable(span)
235            | Self::TooManyArgs(span)
236            | Self::NotEnoughArgs(span)
237            | Self::OutOfRange(span)
238            | Self::WrongUsage { span, .. }
239            | Self::StoreError { span, .. } => *span,
240            Self::Multiple(items) => match items.as_ref() {
241                [] => ast::Span::new((), 0..0),
242                [first, rest @ ..] => {
243                    let mut res = first.span();
244                    for item in rest {
245                        let item_span = item.span();
246                        res.start = std::cmp::min(res.start, item_span.start);
247                        res.end = std::cmp::max(res.end, item_span.end);
248                    }
249                    res
250                }
251            },
252        }
253    }
254}
255
256pub trait JoinResults<T> {
257    fn join_results(self) -> Result<T, AsmError>;
258}
259
260impl<T1, T2> JoinResults<(T1, T2)> for (Result<T1, AsmError>, Result<T2, AsmError>) {
261    fn join_results(self) -> Result<(T1, T2), AsmError> {
262        match self {
263            (Ok(a1), Ok(a2)) => Ok((a1, a2)),
264            (Ok(_), Err(e)) | (Err(e), Ok(_)) => Err(e),
265            (Err(e1), Err(e2)) => Err(AsmError::Multiple(Box::from([e1, e2]))),
266        }
267    }
268}
269
270impl<T1, T2, T3> JoinResults<(T1, T2, T3)>
271    for (
272        Result<T1, AsmError>,
273        Result<T2, AsmError>,
274        Result<T3, AsmError>,
275    )
276{
277    fn join_results(self) -> Result<(T1, T2, T3), AsmError> {
278        match self {
279            (Ok(a1), Ok(a2), Ok(a3)) => Ok((a1, a2, a3)),
280            (Ok(_), Ok(_), Err(e)) | (Ok(_), Err(e), Ok(_)) | (Err(e), Ok(_), Ok(_)) => Err(e),
281            (Ok(_), Err(e1), Err(e2)) | (Err(e1), Err(e2), Ok(_)) | (Err(e1), Ok(_), Err(e2)) => {
282                Err(AsmError::Multiple(Box::from([e1, e2])))
283            }
284            (Err(e1), Err(e2), Err(e3)) => Err(AsmError::Multiple(Box::from([e1, e2, e3]))),
285        }
286    }
287}