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}