Add "check" mode

Input::File(String) -> Input::file(PathBuf)
This commit is contained in:
Shunsuke Shibayama 2022-09-21 14:01:20 +09:00
parent c1d92bc0f4
commit 9b0d66a63a
16 changed files with 200 additions and 184 deletions

View file

@ -4,7 +4,9 @@
use std::env;
use std::fs::File;
use std::io::{stdin, BufRead, BufReader, Read};
use std::path::PathBuf;
use std::process;
use std::str::FromStr;
use crate::stdin::GLOBAL_STDIN;
use crate::{power_assert, read_file};
@ -17,8 +19,7 @@ pub const BUILD_DATE: &str = env!("BUILD_DATE");
/// Inputで操作を一本化する
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum Input {
/// filename
File(String),
File(PathBuf),
REPL,
/// same content as cfg.command
Pipe(String),
@ -34,7 +35,7 @@ impl Input {
pub fn enclosed_name(&self) -> &str {
match self {
Self::File(filename) => &filename[..],
Self::File(filename) => filename.as_os_str().to_str().unwrap_or("???"),
Self::REPL | Self::Pipe(_) => "<stdin>",
Self::Str(_) => "<string>",
Self::Dummy => "<dummy>",
@ -44,7 +45,7 @@ impl Input {
/// ファイルに書き出すとき使う
pub fn filename(&self) -> &str {
match self {
Self::File(filename) => &filename[..],
Self::File(filename) => filename.as_os_str().to_str().unwrap_or("???"),
Self::REPL | Self::Pipe(_) => "stdin",
Self::Str(_) => "string",
Self::Dummy => "dummy",
@ -54,11 +55,14 @@ impl Input {
pub fn read(&self) -> String {
match self {
Self::File(filename) => {
let file = match File::open(&filename[..]) {
let file = match File::open(filename) {
Ok(f) => f,
Err(e) => {
let code = e.raw_os_error().unwrap_or(1);
println!("cannot open '{filename}': [Errno {code}] {e}");
println!(
"cannot open '{}': [Errno {code}] {e}",
filename.to_string_lossy()
);
process::exit(code);
}
};
@ -66,7 +70,10 @@ impl Input {
Ok(s) => s,
Err(e) => {
let code = e.raw_os_error().unwrap_or(1);
println!("cannot read '{filename}': [Errno {code}] {e}");
println!(
"cannot read '{}': [Errno {code}] {e}",
filename.to_string_lossy()
);
process::exit(code);
}
}
@ -80,7 +87,7 @@ impl Input {
pub fn reread_lines(&self, ln_begin: usize, ln_end: usize) -> Vec<String> {
power_assert!(ln_begin, >=, 1);
match self {
Self::File(filename) => match File::open(&filename[..]) {
Self::File(filename) => match File::open(filename) {
Ok(file) => {
let mut codes = vec![];
let mut lines = BufReader::new(file).lines().skip(ln_begin - 1);
@ -238,7 +245,10 @@ impl ErgConfig {
panic!("invalid option: {other}");
}
_ => {
cfg.input = Input::File(arg);
cfg.input = Input::File(
PathBuf::from_str(&arg[..])
.unwrap_or_else(|_| panic!("invalid file path: {}", arg)),
);
break;
}
}

View file

@ -218,6 +218,12 @@ macro_rules! impl_stream_for_wrapper {
}
}
impl From<$Strc> for Vec<$Inner> {
fn from(item: $Strc) -> Vec<$Inner> {
item.payload()
}
}
impl $crate::traits::Stream<$Inner> for $Strc {
#[inline]
fn payload(self) -> Vec<$Inner> {
@ -259,6 +265,12 @@ macro_rules! impl_stream {
erg_common::traits::Stream::get(self, idx).unwrap()
}
}
impl From<$Strc> for Vec<$Inner> {
fn from(item: $Strc) -> Vec<$Inner> {
item.payload()
}
}
};
}

View file

@ -1,5 +1,5 @@
use erg_common::config::{ErgConfig, Input};
use erg_common::traits::Stream;
use erg_common::traits::{Runnable, Stream};
use erg_parser::ast::VarName;
use erg_parser::builder::ASTBuilder;
@ -19,20 +19,20 @@ pub struct HIRBuilder {
impl HIRBuilder {
fn convert(&self, errs: TyCheckErrors) -> CompileErrors {
errs.into_iter()
.map(|e| CompileError::new(e.core, self.checker.cfg.input.clone(), e.caused_by))
.map(|e| CompileError::new(e.core, self.checker.input().clone(), e.caused_by))
.collect::<Vec<_>>()
.into()
}
pub fn new(cfg: ErgConfig, mod_cache: SharedModuleCache) -> Self {
Self {
checker: Checker::new(cfg, mod_cache.clone()),
checker: Checker::new_with_cache(cfg, mod_cache.clone()),
mod_cache,
}
}
pub fn build_and_cache(&mut self, var_name: VarName) -> Result<(), CompileErrors> {
let mut ast_builder = ASTBuilder::new(self.checker.cfg.copy());
let mut ast_builder = ASTBuilder::new(self.checker.cfg().copy());
let ast = ast_builder.build()?;
let ast = Reorderer::new()
.reorder(ast)
@ -46,7 +46,7 @@ impl HIRBuilder {
}
pub fn build_and_cache_main(&mut self, src: String, mode: &str) -> Result<(), CompileErrors> {
let mut cfg = self.checker.cfg.copy();
let mut cfg = self.checker.cfg().copy();
cfg.input = Input::Str(src);
let mut ast_builder = ASTBuilder::new(cfg);
let ast = ast_builder.build()?;

View file

@ -16,50 +16,22 @@ use crate::ownercheck::OwnershipChecker;
/// Summarize lowering, side-effect checking, and ownership checking
#[derive(Debug)]
pub struct Checker {
pub cfg: ErgConfig,
lowerer: ASTLowerer,
ownership_checker: OwnershipChecker,
}
impl Checker {
pub fn new(cfg: ErgConfig, mod_cache: SharedModuleCache) -> Self {
Self {
cfg,
lowerer: ASTLowerer::new(mod_cache),
ownership_checker: OwnershipChecker::new(),
}
}
pub fn check(&mut self, ast: AST, mode: &str) -> Result<(HIR, Context), TyCheckErrors> {
let (hir, ctx, warns) = self.lowerer.lower(ast, mode)?;
if self.cfg.verbose >= 2 {
warns.fmt_all_stderr();
}
let effect_checker = SideEffectChecker::new();
let hir = effect_checker.check(hir)?;
let hir = self.ownership_checker.check(hir)?;
Ok((hir, ctx))
}
}
pub struct CheckerRunner {
checker: Checker,
}
impl Runnable for CheckerRunner {
impl Runnable for Checker {
type Err = TyCheckError;
type Errs = TyCheckErrors;
const NAME: &'static str = "Erg type-checker";
fn new(cfg: ErgConfig) -> Self {
Self {
checker: Checker::new(cfg, SharedModuleCache::new()),
}
Checker::new_with_cache(cfg, SharedModuleCache::new())
}
#[inline]
fn cfg(&self) -> &ErgConfig {
&self.checker.cfg
self.lowerer.cfg()
}
#[inline]
@ -70,7 +42,7 @@ impl Runnable for CheckerRunner {
fn exec(&mut self) -> Result<(), Self::Errs> {
let mut builder = ASTBuilder::new(self.cfg().copy());
let ast = builder.build()?;
let (hir, _) = self.checker.check(ast, "exec")?;
let (hir, _) = self.check(ast, "exec")?;
println!("{hir}");
Ok(())
}
@ -78,7 +50,27 @@ impl Runnable for CheckerRunner {
fn eval(&mut self, src: String) -> Result<String, TyCheckErrors> {
let mut builder = ASTBuilder::new(self.cfg().copy());
let ast = builder.build_with_str(src)?;
let (hir, _) = self.checker.check(ast, "eval")?;
let (hir, _) = self.check(ast, "eval")?;
Ok(hir.to_string())
}
}
impl Checker {
pub fn new_with_cache(cfg: ErgConfig, mod_cache: SharedModuleCache) -> Self {
Self {
lowerer: ASTLowerer::new_with_cache(cfg, mod_cache),
ownership_checker: OwnershipChecker::new(),
}
}
pub fn check(&mut self, ast: AST, mode: &str) -> Result<(HIR, Context), TyCheckErrors> {
let (hir, ctx, warns) = self.lowerer.lower(ast, mode)?;
if self.cfg().verbose >= 2 {
warns.fmt_all_stderr();
}
let effect_checker = SideEffectChecker::new();
let hir = effect_checker.check(hir)?;
let hir = self.ownership_checker.check(hir)?;
Ok((hir, ctx))
}
}

View file

@ -996,7 +996,7 @@ impl CodeGenerator {
self.write_instr(Opcode::LOAD_BUILD_CLASS);
self.write_arg(0);
self.stack_inc();
let code = self.emit_typedef_block(class_def);
let code = self.emit_class_block(class_def);
self.emit_load_const(code);
self.emit_load_const(ident.inspect().clone());
self.write_instr(Opcode::MAKE_FUNCTION);
@ -1014,6 +1014,7 @@ impl CodeGenerator {
// NOTE: use `TypeVar`, `Generic` in `typing` module
// fn emit_poly_type_def(&mut self, sig: SubrSignature, body: DefBody) {}
/// Y = Inherit X => class Y(X): ...
fn emit_require_type(&mut self, kind: TypeKind, require_or_sup: Expr) -> usize {
log!(info "entered {} ({kind:?}, {require_or_sup})", fn_name!());
match kind {
@ -1638,17 +1639,13 @@ impl CodeGenerator {
self.cancel_pop_top();
}
fn emit_typedef_block(&mut self, class: ClassDef) -> CodeObj {
fn emit_class_block(&mut self, class: ClassDef) -> CodeObj {
log!(info "entered {}", fn_name!());
let name = class.sig.ident().inspect().clone();
self.unit_size += 1;
let firstlineno = match (
class.private_methods.get(0).and_then(|def| def.ln_begin()),
class.public_methods.get(0).and_then(|def| def.ln_begin()),
) {
(Some(l), Some(r)) => l.min(r),
(Some(line), None) | (None, Some(line)) => line,
(None, None) => class.sig.ln_begin().unwrap(),
let firstlineno = match class.methods.get(0).and_then(|def| def.ln_begin()) {
Some(l) => l,
None => class.sig.ln_begin().unwrap(),
};
self.units.push(CodeGenUnit::new(
self.unit_size,
@ -1660,33 +1657,13 @@ impl CodeGenerator {
let mod_name = self.toplevel_block_codeobj().name.clone();
self.emit_load_const(mod_name);
self.emit_store_instr(Identifier::public("__module__"), Name);
self.emit_load_const(name.clone());
self.emit_load_const(name);
self.emit_store_instr(Identifier::public("__qualname__"), Name);
self.emit_init_method(&class.sig, class.__new__.clone());
if class.need_to_gen_new {
self.emit_new_func(&class.sig, class.__new__);
}
for def in class.private_methods.into_iter() {
match def.sig {
Signature::Subr(sig) => self.emit_subr_def(Some(&name[..]), sig, def.body),
Signature::Var(sig) => self.emit_var_def(sig, def.body),
}
// TODO: discard
if self.cur_block().stack_len == 1 {
self.emit_pop_top();
}
}
for mut def in class.public_methods.into_iter() {
def.sig.ident_mut().dot = Some(Token::dummy());
match def.sig {
Signature::Subr(sig) => self.emit_subr_def(Some(&name[..]), sig, def.body),
Signature::Var(sig) => self.emit_var_def(sig, def.body),
}
// TODO: discard
if self.cur_block().stack_len == 1 {
self.emit_pop_top();
}
}
self.emit_frameless_block(class.methods, vec![]);
self.emit_load_const(ValueObj::None);
self.write_instr(RETURN_VALUE);
self.write_arg(0u8);

View file

@ -1,4 +1,5 @@
use std::option::Option; // conflicting to Type::Option
use std::option::Option;
use std::path::PathBuf; // conflicting to Type::Option
use erg_common::config::{ErgConfig, Input};
use erg_common::error::MultiErrorDisplay;
@ -762,6 +763,7 @@ impl Context {
pub(crate) fn import_mod(
&mut self,
current_input: Input,
var_name: &VarName,
mod_name: &hir::Expr,
) -> TyCheckResult<()> {
@ -812,7 +814,12 @@ impl Context {
Self::init_py_time_mod(),
);
}
_ => self.import_user_module(var_name, __name__, mod_cache),
_ => self.import_user_module(
current_input,
var_name,
__name__,
mod_cache,
),
}
} else {
// maybe unreachable
@ -843,9 +850,16 @@ impl Context {
Ok(())
}
fn import_user_module(&self, var_name: &VarName, __name__: Str, mod_cache: &SharedModuleCache) {
fn import_user_module(
&self,
_current_input: Input,
var_name: &VarName,
__name__: Str,
mod_cache: &SharedModuleCache,
) {
let path = PathBuf::from(format!("{__name__}.er"));
let cfg = ErgConfig {
input: Input::File(format!("{__name__}.er")),
input: Input::File(path),
..ErgConfig::default()
};
let mut hir_builder = HIRBuilder::new(cfg, mod_cache.clone());

View file

@ -476,19 +476,9 @@ impl Context {
}
Ok(())
}
hir::Expr::ClassDef(type_def) => {
for def in type_def.public_methods.iter_mut() {
match &mut def.sig {
hir::Signature::Var(var) => {
var.t = self.deref_tyvar(mem::take(&mut var.t), var.loc())?;
}
hir::Signature::Subr(subr) => {
subr.t = self.deref_tyvar(mem::take(&mut subr.t), subr.loc())?;
}
}
for chunk in def.body.block.iter_mut() {
self.resolve_expr_t(chunk)?;
}
hir::Expr::ClassDef(class_def) => {
for def in class_def.methods.iter_mut() {
self.resolve_expr_t(def)?;
}
Ok(())
}

View file

@ -84,10 +84,10 @@ impl SideEffectChecker {
Expr::Def(def) => {
self.check_def(def);
}
Expr::ClassDef(type_def) => {
Expr::ClassDef(class_def) => {
// TODO: grow
for def in type_def.public_methods.iter() {
self.check_def(def);
for def in class_def.methods.iter() {
self.check_expr(def);
}
}
Expr::Call(call) => {
@ -211,9 +211,9 @@ impl SideEffectChecker {
Expr::Def(def) => {
self.check_def(def);
}
Expr::ClassDef(type_def) => {
for def in type_def.public_methods.iter() {
self.check_def(def);
Expr::ClassDef(class_def) => {
for def in class_def.methods.iter() {
self.check_expr(def);
}
}
Expr::Array(array) => match array {

View file

@ -1390,16 +1390,14 @@ pub struct ClassDef {
/// The type of `new` that is automatically defined if not defined
pub need_to_gen_new: bool,
pub __new__: Type,
pub private_methods: RecordAttrs,
pub public_methods: RecordAttrs,
pub methods: Block,
}
impl NestedDisplay for ClassDef {
fn fmt_nest(&self, f: &mut fmt::Formatter<'_>, level: usize) -> fmt::Result {
self.sig.fmt_nest(f, level)?;
writeln!(f, ":")?;
self.private_methods.fmt_nest(f, level + 1)?;
self.public_methods.fmt_nest(f, level + 1)
self.methods.fmt_nest(f, level + 1)
}
}
@ -1432,8 +1430,7 @@ impl ClassDef {
require_or_sup: Expr,
need_to_gen_new: bool,
__new__: Type,
private_methods: RecordAttrs,
public_methods: RecordAttrs,
methods: Block,
) -> Self {
Self {
kind,
@ -1441,8 +1438,7 @@ impl ClassDef {
require_or_sup: Box::new(require_or_sup),
need_to_gen_new,
__new__,
private_methods,
public_methods,
methods,
}
}
}

View file

@ -3,8 +3,8 @@
extern crate erg_common;
pub extern crate erg_parser;
mod builder;
mod check;
pub mod builder;
pub mod check;
mod compile;
pub use compile::*;
mod codegen;

View file

@ -1,19 +1,58 @@
use erg_common::log;
use erg_common::traits::Stream;
use crate::hir::{Expr, HIR};
use crate::mod_cache::SharedModuleCache;
use erg_parser::token::Token;
use erg_type::value::TypeKind;
use erg_type::{SubrKind, SubrType, Type};
use crate::hir::{Block, ClassDef, Expr, Record, RecordAttrs, HIR};
use crate::mod_cache::{ModuleEntry, SharedModuleCache};
pub struct Linker {}
impl Linker {
pub fn link(mod_cache: SharedModuleCache) -> HIR {
log!(info "the linking process has started.");
let mut main_mod_hir = mod_cache.remove("<module>").unwrap().hir.unwrap();
for chunk in main_mod_hir.module.iter_mut() {
match chunk {
Expr::Def(def) if def.def_kind().is_module() => {}
// x = import "mod"
// ↓
// class x:
// ...
Expr::Def(ref def) if def.def_kind().is_module() => {
// let sig = option_enum_unwrap!(&def.sig, Signature::Var)
// .unwrap_or_else(|| todo!("module subroutines are not allowed"));
if let Some(ModuleEntry { hir: Some(hir), .. }) =
mod_cache.remove(def.sig.ident().inspect())
{
let block = Block::new(Vec::from(hir.module));
let def = ClassDef::new(
TypeKind::Class,
def.sig.clone(),
Expr::Record(Record::new(
Token::dummy(),
Token::dummy(),
RecordAttrs::empty(),
)),
false,
Type::Subr(SubrType::new(
SubrKind::Func,
vec![],
None,
vec![],
Type::Failure,
)),
block,
);
*chunk = Expr::ClassDef(def);
}
}
_ => {}
}
}
log!(info "linked: {main_mod_hir}");
main_mod_hir
}
}

View file

@ -31,12 +31,22 @@ use crate::mod_cache::SharedModuleCache;
use crate::varinfo::VarKind;
use Visibility::*;
pub struct ASTLowererRunner {
/// Singleton that checks types of an AST, and convert (lower) it into a HIR
#[derive(Debug)]
pub struct ASTLowerer {
cfg: ErgConfig,
lowerer: ASTLowerer,
pub(crate) ctx: Context,
errs: LowerErrors,
warns: LowerWarnings,
}
impl Runnable for ASTLowererRunner {
impl Default for ASTLowerer {
fn default() -> Self {
Self::new_with_cache(ErgConfig::default(), SharedModuleCache::new())
}
}
impl Runnable for ASTLowerer {
type Err = CompileError;
type Errs = CompileErrors;
const NAME: &'static str = "Erg lowerer";
@ -47,27 +57,21 @@ impl Runnable for ASTLowererRunner {
}
fn new(cfg: ErgConfig) -> Self {
Self {
cfg,
lowerer: ASTLowerer::new(SharedModuleCache::new()),
}
Self::new_with_cache(cfg, SharedModuleCache::new())
}
#[inline]
fn finish(&mut self) {}
fn clear(&mut self) {
self.lowerer.errs.clear();
self.lowerer.warns.clear();
self.errs.clear();
self.warns.clear();
}
fn exec(&mut self) -> Result<(), Self::Errs> {
let mut ast_builder = ASTBuilder::new(self.cfg.copy());
let ast = ast_builder.build()?;
let (hir, _, warns) = self
.lowerer
.lower(ast, "exec")
.map_err(|errs| self.convert(errs))?;
let (hir, _, warns) = self.lower(ast, "exec").map_err(|errs| self.convert(errs))?;
if self.cfg.verbose >= 2 {
let warns = self.convert(warns);
warns.fmt_all_stderr();
@ -79,45 +83,27 @@ impl Runnable for ASTLowererRunner {
fn eval(&mut self, src: String) -> Result<String, CompileErrors> {
let mut ast_builder = ASTBuilder::new(self.cfg.copy());
let ast = ast_builder.build_with_str(src)?;
let (hir, ..) = self
.lowerer
.lower(ast, "eval")
.map_err(|errs| self.convert(errs))?;
let (hir, ..) = self.lower(ast, "eval").map_err(|errs| self.convert(errs))?;
Ok(format!("{hir}"))
}
}
impl ASTLowererRunner {
impl ASTLowerer {
pub fn new_with_cache(cfg: ErgConfig, mod_cache: SharedModuleCache) -> Self {
Self {
cfg,
ctx: Context::new_main_module(mod_cache),
errs: LowerErrors::empty(),
warns: LowerWarnings::empty(),
}
}
fn convert(&self, errs: LowerErrors) -> CompileErrors {
errs.into_iter()
.map(|e| CompileError::new(e.core, self.input().clone(), e.caused_by))
.collect::<Vec<_>>()
.into()
}
}
/// Singleton that checks types of an AST, and convert (lower) it into a HIR
#[derive(Debug)]
pub struct ASTLowerer {
pub(crate) ctx: Context,
errs: LowerErrors,
warns: LowerWarnings,
}
impl Default for ASTLowerer {
fn default() -> Self {
Self::new(SharedModuleCache::new())
}
}
impl ASTLowerer {
pub fn new(mod_cache: SharedModuleCache) -> Self {
Self {
ctx: Context::new_main_module(mod_cache),
errs: LowerErrors::empty(),
warns: LowerWarnings::empty(),
}
}
fn return_t_check(
&self,
@ -601,11 +587,12 @@ impl ASTLowerer {
match block.first().unwrap() {
hir::Expr::Call(call) => {
if call.is_import_call() {
self.ctx
.outer
.as_mut()
.unwrap()
.import_mod(&ident.name, &call.args.pos_args.first().unwrap().expr)?;
let current_input = self.input().clone();
self.ctx.outer.as_mut().unwrap().import_mod(
current_input,
&ident.name,
&call.args.pos_args.first().unwrap().expr,
)?;
}
}
_other => {}
@ -660,8 +647,7 @@ impl ASTLowerer {
fn lower_class_def(&mut self, class_def: ast::ClassDef) -> LowerResult<hir::ClassDef> {
log!(info "entered {}({class_def})", fn_name!());
let mut hir_def = self.lower_def(class_def.def)?;
let mut private_methods = hir::RecordAttrs::empty();
let mut public_methods = hir::RecordAttrs::empty();
let mut hir_methods = hir::Block::empty();
for mut methods in class_def.methods_list.into_iter() {
let (class, impl_trait) = match &methods.class {
ast::TypeSpec::TypeApp { spec, args } => {
@ -731,19 +717,11 @@ impl ASTLowerer {
self.ctx.preregister_def(def)?;
}
for def in methods.defs.into_iter() {
if methods.vis.is(TokenKind::Dot) {
let def = self.lower_def(def).map_err(|e| {
self.pop_append_errs();
e
})?;
public_methods.push(def);
} else {
let def = self.lower_def(def).map_err(|e| {
self.pop_append_errs();
e
})?;
private_methods.push(def);
}
hir_methods.push(hir::Expr::Def(def));
}
match self.ctx.pop() {
Ok(methods) => {
@ -782,8 +760,7 @@ impl ASTLowerer {
require_or_sup,
need_to_gen_new,
__new__,
private_methods,
public_methods,
hir_methods,
))
}

View file

@ -8,7 +8,8 @@ use std::thread;
use erg_common::config::ErgConfig;
use erg_common::traits::Runnable;
use erg_compiler::lower::ASTLowererRunner;
use erg_compiler::check::Checker;
use erg_compiler::lower::ASTLowerer;
use erg_compiler::Compiler;
use erg_parser::lex::LexerRunner;
@ -26,7 +27,10 @@ fn run() {
ParserRunner::run(cfg);
}
"lower" => {
ASTLowererRunner::run(cfg);
ASTLowerer::run(cfg);
}
"check" => {
Checker::run(cfg);
}
"compile" | "exec" => {
Compiler::run(cfg);

View file

@ -2916,6 +2916,7 @@ impl Def {
DefKind::Other
}
}
Some("import") => DefKind::Module,
_ => DefKind::Other,
},
_ => DefKind::Other,

View file

@ -124,8 +124,8 @@ impl Deserializer {
eprintln!("{:?} is not a filename", cfg.input);
process::exit(1);
};
let codeobj = CodeObj::from_pyc(&filename[..])
.unwrap_or_else(|_| panic!("failed to deserialize {filename}"));
let codeobj = CodeObj::from_pyc(&filename)
.unwrap_or_else(|_| panic!("failed to deserialize {}", filename.to_string_lossy()));
println!("{}", codeobj.code_info());
}

View file

@ -11,7 +11,8 @@ use erg_common::traits::Runnable;
use erg_parser::lex::LexerRunner;
use erg_parser::ParserRunner;
use erg_compiler::lower::ASTLowererRunner;
use erg_compiler::check::Checker;
use erg_compiler::lower::ASTLowerer;
use erg_compiler::Compiler;
use erg_type::deserialize::Deserializer;
@ -28,7 +29,10 @@ fn run() {
ParserRunner::run(cfg);
}
"lower" => {
ASTLowererRunner::run(cfg);
ASTLowerer::run(cfg);
}
"check" => {
Checker::run(cfg);
}
"compile" => {
Compiler::run(cfg);