kb/
trans.rs

1use super::ast::*;
2use super::ArgSpec;
3use super::ArithmeticBinop;
4use super::BasicError;
5use super::Binop;
6use super::Code;
7use super::Opcode;
8use super::RcStr;
9use super::VarScope;
10use super::PRELUDE_NAME;
11use std::collections::HashMap;
12use std::collections::HashSet;
13use std::rc::Rc;
14
15pub fn translate_files(mut files: Vec<File>) -> Result<Code, BasicError> {
16    // enumerate all variables in files and functions,
17    // compute full/unique names for all variables and functions
18    let mut global_vars = Vars::new();
19    for file in &mut files {
20        prepare_vars_for_file(&mut global_vars, file)?;
21    }
22
23    // initialize scope with all global variables
24    let mut scope = Scope::new();
25    for file in &files {
26        for imp in &file.imports {
27            scope.decl(Item::Import(imp.clone()))?;
28        }
29    }
30    for var in &global_vars.list {
31        scope.decl(Item::Var(var.clone()))?;
32    }
33
34    let mut code = Code::new(
35        false,
36        format!("[main]").into(),
37        ArgSpec::empty(),
38        global_vars.list,
39    );
40
41    // translate all statements inside functions and
42    // initialize all functions at the global scope
43    for file in &files {
44        scope.file_name = file.name().clone();
45        for func in &file.funcs {
46            let func_code = translate_func(&mut scope, func)?;
47            code.add(Opcode::NewFunc(Rc::new(func_code)), func.mark.clone());
48            if func.test {
49                code.add(Opcode::AddToTest, func.mark.clone());
50            }
51            let var = func.as_var.as_ref().unwrap();
52            code.add(Opcode::Set(var.vscope, var.index), func.mark.clone());
53        }
54    }
55
56    // translate all other global statements
57    for file in &files {
58        scope.file_name = file.name().clone();
59        translate_stmt(&mut code, &mut scope, &file.body)?;
60    }
61
62    code.resolve_labels()?;
63
64    Ok(code)
65}
66
67struct Vars {
68    list: Vec<Var>,
69    map: HashMap<RcStr, Var>,
70}
71
72impl Vars {
73    fn new() -> Self {
74        Self {
75            list: vec![],
76            map: HashMap::new(),
77        }
78    }
79    fn add(&mut self, var: Var) {
80        if !self.map.contains_key(&var.name) {
81            self.force_add(var).unwrap();
82        }
83    }
84    fn force_add(&mut self, var: Var) -> Result<(), BasicError> {
85        assert_eq!(self.list.len(), self.map.len());
86        if let Some(oldvar) = self.map.get(&var.name) {
87            Err(BasicError::new_with_help(
88                vec![var.mark.clone(), oldvar.mark.clone()],
89                format!("Conflicting definitions of {}", var.name),
90                format!(concat!(
91                    "To reduce the chance of confusion, it is considered an ",
92                    "error for a function to share a name with a normal variable ",
93                    "or even another function.",
94                )),
95            ))
96        } else {
97            self.map.insert(var.name.clone(), var.clone());
98            self.list.push(var);
99            Ok(())
100        }
101    }
102    fn len(&self) -> usize {
103        assert_eq!(self.list.len(), self.map.len());
104        self.list.len()
105    }
106}
107
108fn prepare_vars_for_file(out: &mut Vars, file: &mut File) -> Result<(), BasicError> {
109    let file_name = file.name().clone();
110
111    for imp in &mut file.imports {
112        imp.unique_name = format!("{}#{}", file_name, imp.alias).into();
113    }
114
115    prepare_vars_for_stmt(out, &mut file.body, Some(&file_name))?;
116
117    // while we allow variables to be assigned to multiple times,
118    // we disallow functions to share names with normal variables
119    // or even other functions.
120    //
121    // To enforce this, we add the function variables last, and
122    // raise an error if we encounter any conflicts
123
124    for func in &mut file.funcs {
125        prepare_vars_for_func(func)?;
126        let var = mkvar(
127            func.mark.clone(),
128            &func.short_name,
129            Some(&file_name),
130            out.len(),
131        );
132        func.as_var = Some(var.clone());
133        out.force_add(var)?;
134    }
135
136    Ok(())
137}
138
139fn prepare_vars_for_func(func: &mut FuncDisplay) -> Result<(), BasicError> {
140    let mut vars = Vars::new();
141    let spec = &func.argspec;
142    for param in &spec.req {
143        let var = mkvar(func.mark.clone(), param, None, vars.len());
144        vars.force_add(var)?;
145    }
146    for (param, _) in &spec.def {
147        let var = mkvar(func.mark.clone(), param, None, vars.len());
148        vars.force_add(var)?;
149    }
150    if let Some(param) = &spec.var {
151        let var = mkvar(func.mark.clone(), param, None, vars.len());
152        vars.force_add(var)?;
153    }
154    prepare_vars_for_stmt(&mut vars, &mut func.body, None)?;
155    func.vars = vars.list;
156    Ok(())
157}
158
159fn prepare_vars_for_stmt(
160    out: &mut Vars,
161    stmt: &mut Stmt,
162    prefix: Option<&RcStr>,
163) -> Result<(), BasicError> {
164    match &mut stmt.desc {
165        StmtDesc::Block(stmts) => {
166            for stmt in stmts {
167                prepare_vars_for_stmt(out, stmt, prefix)?;
168            }
169        }
170        StmtDesc::Assign(target, other_targets, _) => {
171            prepare_vars_for_target(out, target, prefix)?;
172            for target in other_targets {
173                prepare_vars_for_target(out, target, prefix)?;
174            }
175        }
176        StmtDesc::If(pairs, other) => {
177            for (_cond, body) in pairs {
178                prepare_vars_for_stmt(out, body, prefix)?;
179            }
180            if let Some(other) = other {
181                prepare_vars_for_stmt(out, other, prefix)?;
182            }
183        }
184        StmtDesc::While(_cond, body) => {
185            prepare_vars_for_stmt(out, body, prefix)?;
186        }
187        StmtDesc::ForIn(target, _container, body) => {
188            prepare_vars_for_target(out, target, prefix)?;
189            prepare_vars_for_stmt(out, body, prefix)?;
190        }
191        StmtDesc::ForClassic(target, _start, _end, _inclusive, _step, body) => {
192            prepare_vars_for_target(out, target, prefix)?;
193            prepare_vars_for_stmt(out, body, prefix)?;
194        }
195        StmtDesc::Print(_)
196        | StmtDesc::Assert(_)
197        | StmtDesc::Expr(_)
198        | StmtDesc::Return(_)
199        | StmtDesc::Label(_)
200        | StmtDesc::Goto(_) => {}
201    }
202    Ok(())
203}
204
205fn prepare_vars_for_target(
206    out: &mut Vars,
207    target: &AssignTarget,
208    prefix: Option<&RcStr>,
209) -> Result<(), BasicError> {
210    match &target.desc {
211        AssignTargetDesc::Name(name) => {
212            out.add(mkvar(target.mark.clone(), name, prefix, out.len()));
213        }
214        AssignTargetDesc::List(list) => {
215            for subtarget in list {
216                prepare_vars_for_target(out, subtarget, prefix)?;
217            }
218        }
219    }
220    Ok(())
221}
222
223fn mkvar(mark: Mark, name: &RcStr, file_name: Option<&RcStr>, index: usize) -> Var {
224    let index = index as u32;
225    if let Some(file_name) = file_name {
226        Var {
227            mark: mark,
228            name: format!("{}#{}", file_name, name).into(),
229            vscope: VarScope::Global,
230            index,
231        }
232    } else {
233        Var {
234            mark: mark,
235            name: name.clone(),
236            vscope: VarScope::Local,
237            index,
238        }
239    }
240}
241
242fn translate_func(scope: &mut Scope, func: &FuncDisplay) -> Result<Code, BasicError> {
243    let mut code = Code::new(
244        func.generator,
245        func.full_name().clone(),
246        func.argspec.clone(),
247        func.vars.clone(),
248    );
249    scope.enter_local();
250    for var in &func.vars {
251        scope.decl(Item::Var(var.clone()))?;
252    }
253    translate_stmt(&mut code, scope, &func.body)?;
254    scope.exit_local();
255    code.resolve_labels()?;
256    Ok(code)
257}
258
259fn translate_stmt(code: &mut Code, scope: &mut Scope, stmt: &Stmt) -> Result<(), BasicError> {
260    match &stmt.desc {
261        StmtDesc::Block(stmts) => {
262            for child_stmt in stmts {
263                translate_stmt(code, scope, child_stmt)?;
264            }
265        }
266        StmtDesc::Return(expr) => {
267            if let Some(expr) = expr {
268                translate_expr(code, scope, expr)?;
269            } else {
270                code.add(Opcode::Nil, stmt.mark.clone());
271            }
272            code.add(Opcode::Return, stmt.mark.clone());
273        }
274        StmtDesc::Assign(target, other_targets, expr) => {
275            translate_expr(code, scope, expr)?;
276            for target in other_targets.iter().rev() {
277                translate_assign(code, scope, target, false)?;
278            }
279            translate_assign(code, scope, target, true)?;
280        }
281        StmtDesc::Expr(expr) => {
282            translate_expr(code, scope, expr)?;
283            code.add(Opcode::Pop, stmt.mark.clone());
284        }
285        StmtDesc::Print(arg) => {
286            translate_expr(code, scope, arg)?;
287            code.add(Opcode::Print, stmt.mark.clone());
288        }
289        StmtDesc::Assert(arg) => match &arg.desc {
290            ExprDesc::Binop(Binop::Equal, lhs, rhs) => {
291                translate_expr(code, scope, lhs)?;
292                translate_expr(code, scope, rhs)?;
293                code.add(Opcode::AssertEq, stmt.mark.clone());
294            }
295            _ => {
296                translate_expr(code, scope, arg)?;
297                code.add(Opcode::Assert, stmt.mark.clone());
298            }
299        },
300        StmtDesc::Label(label) => {
301            code.add(Opcode::Label(label.clone()), stmt.mark.clone());
302        }
303        StmtDesc::Goto(label) => {
304            code.add(Opcode::UnresolvedGoto(label.clone()), stmt.mark.clone());
305        }
306        StmtDesc::If(pairs, other) => {
307            let end_label = scope.new_label();
308            for (cond, body) in pairs {
309                let next_label = scope.new_label();
310                translate_expr(code, scope, cond)?;
311                code.add(
312                    Opcode::UnresolvedGotoIfFalse(next_label.clone()),
313                    cond.mark.clone(),
314                );
315                translate_stmt(code, scope, body)?;
316                code.add(Opcode::UnresolvedGoto(end_label.clone()), body.mark.clone());
317                code.add(Opcode::Label(next_label), cond.mark.clone());
318            }
319            if let Some(other) = other {
320                translate_stmt(code, scope, other)?;
321            }
322            code.add(Opcode::Label(end_label), stmt.mark.clone());
323        }
324        StmtDesc::While(cond, body) => {
325            let start_label = scope.new_label();
326            let end_label = scope.new_label();
327            code.add(Opcode::Label(start_label.clone()), stmt.mark.clone());
328            translate_expr(code, scope, cond)?;
329            code.add(
330                Opcode::UnresolvedGotoIfFalse(end_label.clone()),
331                cond.mark.clone(),
332            );
333            translate_stmt(code, scope, body)?;
334            code.add(Opcode::UnresolvedGoto(start_label), stmt.mark.clone());
335            code.add(Opcode::Label(end_label), stmt.mark.clone());
336        }
337        StmtDesc::ForIn(target, container, body) => {
338            let start_label = scope.new_label();
339            let end_label = scope.new_label();
340
341            translate_expr(code, scope, container)?;
342
343            code.add(Opcode::Label(start_label.clone()), stmt.mark.clone());
344            code.add(Opcode::Next, stmt.mark.clone());
345            code.add(
346                Opcode::UnresolvedGotoIfFalse(end_label.clone()),
347                stmt.mark.clone(),
348            );
349
350            translate_assign(code, scope, target, true)?;
351            translate_stmt(code, scope, body)?;
352            code.add(Opcode::UnresolvedGoto(start_label), stmt.mark.clone());
353
354            code.add(Opcode::Label(end_label), stmt.mark.clone());
355            code.add(Opcode::Pop, stmt.mark.clone()); // pop nil
356            code.add(Opcode::Pop, stmt.mark.clone()); // pop genobj
357        }
358        StmtDesc::ForClassic(target, start, end, inclusive, step, body) => {
359            let start_label = scope.new_label();
360            let end_label = scope.new_label();
361
362            translate_expr(code, scope, end)?;
363            translate_expr(code, scope, start)?;
364
365            code.add(Opcode::Label(start_label.clone()), stmt.mark.clone());
366
367            // Test that start <-> end
368            // NOTE: start is at TOS, so operators need to be 'flipped'
369            code.add(Opcode::Dup2, stmt.mark.clone());
370            code.add(
371                Opcode::Binop(if *step < 0.0 {
372                    if *inclusive {
373                        Binop::LessThanOrEqual
374                    } else {
375                        Binop::LessThan
376                    }
377                } else if *step > 0.0 {
378                    if *inclusive {
379                        Binop::GreaterThanOrEqual
380                    } else {
381                        Binop::GreaterThan
382                    }
383                } else {
384                    return Err(BasicError::new(
385                        vec![stmt.mark.clone()],
386                        format!("The STEP of a FOR loop cannot be zero"),
387                    ));
388                }),
389                stmt.mark.clone(),
390            );
391            code.add(
392                Opcode::UnresolvedGotoIfFalse(end_label.clone()),
393                stmt.mark.clone(),
394            );
395
396            // assign 'start' to 'target'
397            translate_assign(code, scope, target, false)?;
398
399            translate_stmt(code, scope, body)?;
400
401            code.add(Opcode::Number(*step), stmt.mark.clone());
402            code.add(
403                Opcode::Binop(Binop::Arithmetic(ArithmeticBinop::Add)),
404                stmt.mark.clone(),
405            );
406            code.add(Opcode::UnresolvedGoto(start_label), stmt.mark.clone());
407
408            code.add(Opcode::Label(end_label), stmt.mark.clone());
409            code.add(Opcode::Pop, stmt.mark.clone()); // pop start
410            code.add(Opcode::Pop, stmt.mark.clone()); // pop end
411        }
412    }
413    Ok(())
414}
415
416/// When called, assumes the value to assign to the variable is on the top of the stack
417/// the 'consume' flag whether the top of the stack value will be consumed
418fn translate_assign(
419    code: &mut Code,
420    scope: &mut Scope,
421    target: &AssignTarget,
422    consume: bool,
423) -> Result<(), BasicError> {
424    match &target.desc {
425        AssignTargetDesc::Name(name) => {
426            let var = scope.getvar_or_error(&target.mark, name)?;
427            if consume {
428                code.add(Opcode::Set(var.vscope, var.index), target.mark.clone());
429            } else {
430                code.add(Opcode::Tee(var.vscope, var.index), target.mark.clone());
431            }
432        }
433        AssignTargetDesc::List(list) => {
434            if !consume {
435                code.add(Opcode::Dup, target.mark.clone());
436            }
437            code.add(Opcode::Unpack(list.len() as u32), target.mark.clone());
438            for subtarget in list.iter().rev() {
439                translate_assign(code, scope, subtarget, true)?;
440            }
441        }
442    }
443    Ok(())
444}
445
446fn translate_expr(code: &mut Code, scope: &mut Scope, expr: &Expr) -> Result<(), BasicError> {
447    match &expr.desc {
448        ExprDesc::Nil => code.add(Opcode::Nil, expr.mark.clone()),
449        ExprDesc::Bool(x) => code.add(Opcode::Bool(*x), expr.mark.clone()),
450        ExprDesc::Number(x) => code.add(Opcode::Number(*x), expr.mark.clone()),
451        ExprDesc::String(x) => code.add(Opcode::String(x.clone()), expr.mark.clone()),
452        ExprDesc::List(items) => {
453            for item in items {
454                translate_expr(code, scope, item)?;
455            }
456            code.add(Opcode::MakeList(items.len() as u32), expr.mark.clone());
457        }
458        ExprDesc::GetVar(name) => {
459            let var = scope.getvar_or_error(&expr.mark, name)?;
460            code.add(Opcode::Get(var.vscope, var.index), expr.mark.clone());
461        }
462        ExprDesc::GetAttr(owner, attr) => {
463            let imp = match &owner.desc {
464                ExprDesc::GetVar(owner_name) => match scope.rget(owner_name) {
465                    Some(Item::Import(imp)) => Some(imp),
466                    _ => None,
467                },
468                _ => None,
469            };
470            if let Some(imp) = imp {
471                // this is accessing an imported variable
472                let full_name = format!("{}#{}", imp.module_name, attr);
473                let var = scope.getvar_or_error(&expr.mark, &full_name)?;
474                code.add(Opcode::Get(var.vscope, var.index), expr.mark.clone());
475            } else {
476                // this is an attribute access
477                return Err(BasicError::new(
478                    vec![expr.mark.clone()],
479                    format!("Attribute access not yet supported"),
480                ));
481            }
482        }
483        ExprDesc::CallFunc(f, args) => {
484            translate_expr(code, scope, f)?;
485            for arg in args {
486                translate_expr(code, scope, arg)?;
487            }
488            code.add(Opcode::CallFunc(args.len() as u32), expr.mark.clone());
489        }
490        ExprDesc::Binop(binop, lhs, rhs) => {
491            translate_expr(code, scope, lhs)?;
492            translate_expr(code, scope, rhs)?;
493            code.add(Opcode::Binop(*binop), expr.mark.clone());
494        }
495        ExprDesc::Unop(unop, subexpr) => {
496            translate_expr(code, scope, subexpr)?;
497            code.add(Opcode::Unop(*unop), expr.mark.clone());
498        }
499        ExprDesc::Yield(yieldexpr) => {
500            translate_expr(code, scope, yieldexpr)?;
501            code.add(Opcode::Yield, expr.mark.clone());
502        }
503        ExprDesc::Next(genexpr) => {
504            translate_expr(code, scope, genexpr)?;
505            code.add(Opcode::Next, expr.mark.clone());
506            code.add(Opcode::MakeList(2), expr.mark.clone());
507        }
508        ExprDesc::Disasm(fexpr) => {
509            translate_expr(code, scope, fexpr)?;
510            code.add(Opcode::Disasm, expr.mark.clone());
511        }
512    }
513    Ok(())
514}
515
516struct Scope {
517    file_name: RcStr,
518    globals: HashMap<RcStr, Item>,
519    locals: Option<HashMap<RcStr, Item>>,
520    labels: Vec<HashSet<RcStr>>,
521}
522
523impl Scope {
524    pub fn new() -> Self {
525        Self {
526            file_name: "".to_owned().into(),
527            globals: HashMap::new(),
528            locals: None,
529            labels: vec![HashSet::new()],
530        }
531    }
532    pub fn decl(&mut self, item: Item) -> Result<(), BasicError> {
533        let map = if let Some(locals) = &mut self.locals {
534            locals
535        } else {
536            &mut self.globals
537        };
538        if let Some(old_item) = map.get(item.name()) {
539            Err(BasicError::new(
540                vec![old_item.mark().clone(), item.mark().clone()],
541                format!("{:?} is defined more than once", item.name()),
542            ))
543        } else {
544            map.insert(item.name().clone(), item);
545            Ok(())
546        }
547    }
548    pub fn getvar_or_error(&self, mark: &Mark, name: &str) -> Result<&Var, BasicError> {
549        match self.rget(name) {
550            None => Err(BasicError::new(
551                vec![mark.clone()],
552                format!("Variable {:?} not found", name),
553            )),
554            Some(Item::Import(..)) => Err(BasicError::new(
555                vec![mark.clone()],
556                format!("{:?} is an import, not a variable", name),
557            )),
558            Some(Item::Var(var)) => Ok(var),
559        }
560    }
561    pub fn rget(&self, name: &str) -> Option<&Item> {
562        self.qget(name)
563            .or_else(|| self.qget(&format!("{}#{}", self.file_name, name)))
564            .or_else(|| self.qget(&format!("{}#{}", PRELUDE_NAME, name)))
565    }
566    pub fn qget(&self, qualified_name: &str) -> Option<&Item> {
567        self.locals
568            .as_ref()
569            .and_then(|locals| locals.get(qualified_name))
570            .or_else(|| self.globals.get(qualified_name))
571    }
572    pub fn new_label(&mut self) -> RcStr {
573        let name: RcStr = format!("#{}", self.labels().len()).into();
574        assert!(!self.labels().contains(&name));
575        self.labels_mut().insert(name.clone());
576        name
577    }
578    pub fn enter_local(&mut self) {
579        if self.locals.is_some() {
580            panic!("Scope::enter_local: already in local scope");
581        }
582        self.locals = Some(HashMap::new());
583        self.labels.push(HashSet::new());
584    }
585    pub fn exit_local(&mut self) {
586        if self.locals.is_none() {
587            panic!("Scope::exit_local: not in local scope");
588        }
589        self.locals = None;
590        self.labels.pop().unwrap();
591    }
592    pub fn labels(&self) -> &HashSet<RcStr> {
593        self.labels.last().unwrap()
594    }
595    pub fn labels_mut(&mut self) -> &mut HashSet<RcStr> {
596        self.labels.last_mut().unwrap()
597    }
598}
599
600#[derive(Debug)]
601enum Item {
602    Var(Var),
603    Import(Import),
604}
605
606impl Item {
607    pub fn mark(&self) -> &Mark {
608        match self {
609            Self::Var(var) => &var.mark,
610            Self::Import(imp) => &imp.mark,
611        }
612    }
613    pub fn name(&self) -> &RcStr {
614        match self {
615            Self::Var(var) => &var.name,
616            Self::Import(imp) => &imp.unique_name,
617        }
618    }
619}