Start re-writing reduced ast and evaluator
This commit is contained in:
parent
e68331fe0a
commit
d8f6c41f04
@ -1,13 +1,15 @@
|
||||
#![allow(clippy::upper_case_acronyms)]
|
||||
#![allow(clippy::enum_variant_names)]
|
||||
|
||||
use std::rc::Rc;
|
||||
use std::fmt;
|
||||
|
||||
mod visitor;
|
||||
mod operators;
|
||||
|
||||
pub use operators::{PrefixOp, BinOp};
|
||||
pub use visitor::{walk_ast, walk_block, ASTVisitor};
|
||||
|
||||
use std::rc::Rc;
|
||||
use crate::derivative::Derivative;
|
||||
use crate::tokenizing::Location;
|
||||
|
||||
@ -15,7 +17,7 @@ use crate::tokenizing::Location;
|
||||
/// the u32 index limits the size of an AST to 2^32 nodes.
|
||||
#[derive(Debug, PartialEq, Eq, Hash, Clone, Default)]
|
||||
pub struct ItemId {
|
||||
idx: u32,
|
||||
pub idx: u32,
|
||||
}
|
||||
|
||||
impl ItemId {
|
||||
@ -24,6 +26,12 @@ impl ItemId {
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for ItemId {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "ItemId:{}", self.idx)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ItemIdStore {
|
||||
last_idx: u32
|
||||
}
|
||||
|
@ -25,6 +25,8 @@ mod builtin;
|
||||
mod error;
|
||||
mod eval;
|
||||
mod reduced_ast;
|
||||
mod reduced_ir;
|
||||
mod tree_walk_eval;
|
||||
|
||||
mod schala;
|
||||
|
||||
|
274
schala-lang/language/src/reduced_ir/mod.rs
Normal file
274
schala-lang/language/src/reduced_ir/mod.rs
Normal file
@ -0,0 +1,274 @@
|
||||
use crate::ast;
|
||||
use crate::symbol_table::{DefId, Symbol, SymbolSpec, SymbolTable};
|
||||
use crate::builtin::Builtin;
|
||||
|
||||
use std::str::FromStr;
|
||||
use std::collections::HashMap;
|
||||
use std::rc::Rc;
|
||||
|
||||
mod test;
|
||||
|
||||
pub fn reduce(ast: &ast::AST, symbol_table: &SymbolTable) -> ReducedIR {
|
||||
let reducer = Reducer::new(symbol_table);
|
||||
reducer.reduce(ast)
|
||||
}
|
||||
|
||||
struct Reducer<'a> {
|
||||
symbol_table: &'a SymbolTable,
|
||||
functions: HashMap<DefId, Callable>,
|
||||
}
|
||||
|
||||
impl<'a> Reducer<'a> {
|
||||
fn new(symbol_table: &'a SymbolTable) -> Self {
|
||||
Self {
|
||||
symbol_table,
|
||||
functions: HashMap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
fn reduce(mut self, ast: &ast::AST) -> ReducedIR {
|
||||
// First reduce all functions
|
||||
// TODO once this works, maybe rewrite it using the Visitor
|
||||
for statement in ast.statements.iter() {
|
||||
self.top_level_statement(&statement);
|
||||
}
|
||||
|
||||
// Then compute the entrypoint statements (which may reference previously-computed
|
||||
// functions by ID)
|
||||
let mut entrypoint = vec![];
|
||||
for statement in ast.statements.iter() {
|
||||
let ast::Statement { id: item_id, kind, .. } = statement;
|
||||
match &kind {
|
||||
ast::StatementKind::Expression(expr) => {
|
||||
entrypoint.push(Statement::Expression(self.expression(&expr)));
|
||||
},
|
||||
ast::StatementKind::Declaration(ast::Declaration::Binding { name, constant, expr, ..}) => {
|
||||
let symbol = self.symbol_table.lookup_symbol(item_id).unwrap();
|
||||
entrypoint.push(Statement::Binding { id: symbol.def_id.clone(), constant: *constant, expr: self.expression(&expr) });
|
||||
},
|
||||
_ => ()
|
||||
}
|
||||
}
|
||||
|
||||
ReducedIR {
|
||||
functions: self.functions,
|
||||
entrypoint,
|
||||
}
|
||||
}
|
||||
|
||||
fn top_level_statement(&mut self, statement: &ast::Statement) {
|
||||
let ast::Statement { id: item_id, kind, .. } = statement;
|
||||
match kind {
|
||||
ast::StatementKind::Expression(_expr) => {
|
||||
//TODO expressions can in principle contain definitions, but I won't worry
|
||||
//about it now
|
||||
()
|
||||
},
|
||||
ast::StatementKind::Declaration(decl) => match decl {
|
||||
ast::Declaration::FuncDecl(_, statements) => {
|
||||
self.insert_function_definition(item_id, statements);
|
||||
},
|
||||
_ => ()
|
||||
},
|
||||
ast::StatementKind::Import(..) => (),
|
||||
ast::StatementKind::Module(modspec) => {
|
||||
//TODO handle modules
|
||||
()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn function_internal_statement(&mut self, statement: &ast::Statement) -> Option<Statement> {
|
||||
let ast::Statement { id: item_id, kind, .. } = statement;
|
||||
match kind {
|
||||
ast::StatementKind::Expression(expr) => {
|
||||
Some(Statement::Expression(self.expression(expr)))
|
||||
},
|
||||
ast::StatementKind::Declaration(decl) => match decl {
|
||||
ast::Declaration::FuncDecl(_, statements) => {
|
||||
self.insert_function_definition(item_id, statements);
|
||||
None
|
||||
},
|
||||
_ => None
|
||||
},
|
||||
_ => None
|
||||
}
|
||||
}
|
||||
|
||||
fn insert_function_definition(&mut self, item_id: &ast::ItemId, statements: &ast::Block) {
|
||||
let symbol = self.symbol_table.lookup_symbol(item_id).unwrap();
|
||||
let def_id = symbol.def_id.clone();
|
||||
let callable = Callable::UserDefined {
|
||||
body: self.function(statements)
|
||||
};
|
||||
self.functions.insert(def_id, callable);
|
||||
}
|
||||
|
||||
fn expression(&mut self, expr: &ast::Expression) -> Expression {
|
||||
use crate::ast::ExpressionKind::*;
|
||||
use Expression::{Unimplemented};
|
||||
|
||||
match &expr.kind {
|
||||
NatLiteral(n) => Expression::Literal(Literal::Nat(*n)),
|
||||
FloatLiteral(f) => Expression::Literal(Literal::Float(*f)),
|
||||
StringLiteral(s) => Expression::Literal(Literal::StringLit(s.clone())),
|
||||
BoolLiteral(b) => Expression::Literal(Literal::Bool(*b)),
|
||||
BinExp(binop, lhs, rhs) => self.binop(binop, lhs, rhs),
|
||||
PrefixExp(op, arg) => self.prefix(op, arg),
|
||||
Value(qualified_name) => self.value(qualified_name),
|
||||
Call { f, arguments } => Unimplemented, // self.reduce_call_expression(f, arguments),
|
||||
TupleLiteral(exprs) => Expression::Tuple(exprs.iter().map(|e| self.expression(e)).collect()),
|
||||
IfExpression {
|
||||
discriminator,
|
||||
body,
|
||||
} => Unimplemented, //self.reduce_if_expression(deref_optional_box(discriminator), body),
|
||||
Lambda { params, body, .. } => Unimplemented, //self.reduce_lambda(params, body),
|
||||
NamedStruct { name, fields } => Unimplemented, //self.reduce_named_struct(name, fields),
|
||||
Index { .. } => Unimplemented,
|
||||
WhileExpression { .. } => Unimplemented,
|
||||
ForExpression { .. } => Unimplemented,
|
||||
ListLiteral { .. } => Unimplemented,
|
||||
}
|
||||
}
|
||||
|
||||
fn function(&mut self, statements: &ast::Block) -> Vec<Statement> {
|
||||
statements.iter().filter_map(|stmt| self.function_internal_statement(stmt)).collect()
|
||||
}
|
||||
|
||||
fn prefix(&mut self, prefix: &ast::PrefixOp, arg: &ast::Expression) -> Expression {
|
||||
let builtin: Option<Builtin> = TryFrom::try_from(prefix).ok();
|
||||
match builtin {
|
||||
Some(op) => {
|
||||
Expression::Call {
|
||||
f: Function::Builtin(op),
|
||||
args: vec![self.expression(arg)],
|
||||
}
|
||||
}
|
||||
None => {
|
||||
//TODO need this for custom prefix ops
|
||||
Expression::Unimplemented
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn binop(&mut self, binop: &ast::BinOp, lhs: &ast::Expression, rhs: &ast::Expression) -> Expression {
|
||||
use Expression::*;
|
||||
let operation = Builtin::from_str(binop.sigil()).ok();
|
||||
match operation {
|
||||
Some(Builtin::Assignment) => {
|
||||
let lval = match &lhs.kind {
|
||||
ast::ExpressionKind::Value(qualified_name) => {
|
||||
if let Some(Symbol { def_id, .. }) = self.symbol_table.lookup_symbol(&qualified_name.id) {
|
||||
def_id.clone()
|
||||
} else {
|
||||
return ReductionError(format!("Couldn't look up name: {:?}", qualified_name));
|
||||
}
|
||||
},
|
||||
_ => return ReductionError("Trying to assign to a non-name".to_string()),
|
||||
};
|
||||
|
||||
Assign {
|
||||
lval,
|
||||
rval: Box::new(self.expression(rhs)),
|
||||
}
|
||||
},
|
||||
Some(op) => {
|
||||
Expression::Call {
|
||||
f: Function::Builtin(op),
|
||||
args: vec![self.expression(lhs), self.expression(rhs)],
|
||||
}
|
||||
}
|
||||
None => {
|
||||
//TODO handle a user-defined operation
|
||||
Unimplemented
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn value(&mut self, qualified_name: &ast::QualifiedName) -> Expression {
|
||||
let ast::QualifiedName { id, components, .. } = qualified_name;
|
||||
println!("Qualified name: {:?}", qualified_name);
|
||||
let maybe_symbol = self.symbol_table.lookup_symbol(&qualified_name.id);
|
||||
println!("Symbol? {:?}", maybe_symbol);
|
||||
|
||||
Expression::Unimplemented
|
||||
}
|
||||
}
|
||||
|
||||
/// The reduced intermediate representation consists of a list of function definitions, and a block
|
||||
/// of entrypoint statements. In a repl or script context this can be an arbitrary list of
|
||||
/// statements, in an executable context will likely just be a pointer to the main() function.
|
||||
#[derive(Debug)]
|
||||
pub struct ReducedIR {
|
||||
functions: HashMap<DefId, Callable>,
|
||||
pub entrypoint: Vec<Statement>,
|
||||
}
|
||||
|
||||
impl ReducedIR {
|
||||
fn debug(&self, symbol_table: &SymbolTable) {
|
||||
println!("Reduced IR:");
|
||||
println!("Functions:");
|
||||
println!("-----------");
|
||||
for (id, callable) in self.functions.iter() {
|
||||
let name = &symbol_table.lookup_symbol_by_def(id).unwrap().local_name;
|
||||
println!("{}({}) -> {:?}", id, name, callable);
|
||||
}
|
||||
println!("");
|
||||
println!("Entrypoint:");
|
||||
println!("-----------");
|
||||
for stmt in self.entrypoint.iter() {
|
||||
println!("{:?}", stmt);
|
||||
}
|
||||
println!("-----------");
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Callable {
|
||||
Builtin(Builtin),
|
||||
UserDefined {
|
||||
body: Vec<Statement>
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Statement {
|
||||
Expression(Expression),
|
||||
Binding {
|
||||
id: DefId,
|
||||
constant: bool,
|
||||
expr: Expression
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Expression {
|
||||
Literal(Literal),
|
||||
Tuple(Vec<Expression>),
|
||||
Assign {
|
||||
lval: DefId,
|
||||
rval: Box<Expression>,
|
||||
},
|
||||
Call {
|
||||
f: Function,
|
||||
args: Vec<Expression>
|
||||
},
|
||||
Unimplemented,
|
||||
ReductionError(String),
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Function {
|
||||
Builtin(Builtin),
|
||||
Defined(DefId),
|
||||
}
|
||||
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Literal {
|
||||
Nat(u64),
|
||||
Int(i64),
|
||||
Float(f64),
|
||||
Bool(bool),
|
||||
StringLit(Rc<String>),
|
||||
}
|
42
schala-lang/language/src/reduced_ir/test.rs
Normal file
42
schala-lang/language/src/reduced_ir/test.rs
Normal file
@ -0,0 +1,42 @@
|
||||
#![cfg(test)]
|
||||
|
||||
use crate::symbol_table::SymbolTable;
|
||||
use super::*;
|
||||
|
||||
fn build_ir(input: &str) -> ReducedIR {
|
||||
let ast = crate::util::quick_ast(input);
|
||||
|
||||
let mut symbol_table = SymbolTable::new();
|
||||
symbol_table.process_ast(&ast).unwrap();
|
||||
|
||||
let reduced = reduce(&ast, &symbol_table);
|
||||
reduced.debug(&symbol_table);
|
||||
reduced
|
||||
}
|
||||
|
||||
|
||||
#[test]
|
||||
fn test_ir() {
|
||||
let src = r#"
|
||||
|
||||
let global_one = 10 + 20
|
||||
let global_two = "the string hello"
|
||||
|
||||
fn a_function(i, j, k) {
|
||||
fn nested(x) {
|
||||
x + 10
|
||||
}
|
||||
i + j * nested(k)
|
||||
}
|
||||
|
||||
fn another_function(e) {
|
||||
let local_var = 420
|
||||
e * local_var
|
||||
}
|
||||
|
||||
another_function()
|
||||
"#;
|
||||
|
||||
let reduced = build_ir(src);
|
||||
assert!(1 == 2);
|
||||
}
|
@ -1,14 +1,14 @@
|
||||
use stopwatch::Stopwatch;
|
||||
|
||||
use crate::error::SchalaError;
|
||||
use crate::{eval, parsing, reduced_ast, symbol_table, tokenizing, typechecking};
|
||||
use crate::{eval, parsing, reduced_ast, reduced_ir, tree_walk_eval, symbol_table, tokenizing, typechecking};
|
||||
use schala_repl::{
|
||||
ComputationRequest, ComputationResponse, GlobalOutputStats, LangMetaRequest, LangMetaResponse,
|
||||
ProgrammingLanguageInterface,
|
||||
};
|
||||
|
||||
/// All the state necessary to parse and execute a Schala program are stored in this struct.
|
||||
pub struct Schala {
|
||||
pub struct Schala<'a> {
|
||||
/// Holds a reference to the original source code, parsed into line and character
|
||||
source_reference: SourceReference,
|
||||
/// Execution state for AST-walking interpreter
|
||||
@ -19,8 +19,12 @@ pub struct Schala {
|
||||
type_context: typechecking::TypeContext<'static>,
|
||||
/// Schala Parser
|
||||
active_parser: parsing::Parser,
|
||||
|
||||
eval_state: tree_walk_eval::State<'a>,
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
impl Schala {
|
||||
//TODO implement documentation for language items
|
||||
/*
|
||||
@ -31,23 +35,25 @@ impl Schala {
|
||||
}
|
||||
*/
|
||||
}
|
||||
*/
|
||||
|
||||
impl Schala {
|
||||
impl<'a> Schala<'a> {
|
||||
/// Creates a new Schala environment *without* any prelude.
|
||||
fn new_blank_env() -> Schala {
|
||||
fn new_blank_env() -> Schala<'a> {
|
||||
Schala {
|
||||
source_reference: SourceReference::new(),
|
||||
symbol_table: symbol_table::SymbolTable::new(),
|
||||
state: eval::State::new(),
|
||||
type_context: typechecking::TypeContext::new(),
|
||||
active_parser: parsing::Parser::new(),
|
||||
eval_state: tree_walk_eval::State::new(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a new Schala environment with the standard prelude, which is defined as ordinary
|
||||
/// Schala code in the file `prelude.schala`
|
||||
#[allow(clippy::new_without_default)]
|
||||
pub fn new() -> Schala {
|
||||
pub fn new() -> Schala<'a> {
|
||||
let prelude = include_str!("../source-files/prelude.schala");
|
||||
let mut env = Schala::new_blank_env();
|
||||
|
||||
@ -88,6 +94,22 @@ impl Schala {
|
||||
.typecheck(&ast)
|
||||
.map_err(SchalaError::from_type_error);
|
||||
|
||||
let reduced_ir = reduced_ir::reduce(&ast, &self.symbol_table);
|
||||
println!("Reduced IR: {:?}", reduced_ir);
|
||||
|
||||
let evaluation_outputs = self.eval_state.evaluate(reduced_ir, true);
|
||||
let text_output: Result<Vec<String>, String> = evaluation_outputs.into_iter().collect();
|
||||
|
||||
let text_output: Result<Vec<String>, SchalaError> =
|
||||
text_output.map_err(|err| SchalaError::from_string(err, Stage::Evaluation));
|
||||
|
||||
let eval_output: String =
|
||||
text_output.map(|v| Iterator::intersperse(v.into_iter(), "\n".to_owned()).collect())?;
|
||||
|
||||
Ok(eval_output)
|
||||
|
||||
/*
|
||||
|
||||
// Reduce AST - TODO this doesn't produce an error yet, but probably should
|
||||
let reduced_ast = reduced_ast::reduce(&ast, &self.symbol_table);
|
||||
|
||||
@ -102,6 +124,7 @@ impl Schala {
|
||||
text_output.map(|v| Iterator::intersperse(v.into_iter(), "\n".to_owned()).collect())?;
|
||||
|
||||
Ok(eval_output)
|
||||
*/
|
||||
}
|
||||
}
|
||||
|
||||
@ -151,7 +174,7 @@ fn stage_names() -> Vec<&'static str> {
|
||||
]
|
||||
}
|
||||
|
||||
impl ProgrammingLanguageInterface for Schala {
|
||||
impl<'a> ProgrammingLanguageInterface for Schala<'a> {
|
||||
//TODO flesh out Config
|
||||
type Config = ();
|
||||
fn language_name() -> String {
|
||||
|
@ -15,6 +15,35 @@ mod symbol_trie;
|
||||
use symbol_trie::SymbolTrie;
|
||||
mod test;
|
||||
|
||||
|
||||
/// ID used for definitions
|
||||
#[derive(Debug, PartialEq, Eq, Hash, Clone, Default)]
|
||||
pub struct DefId {
|
||||
idx: u32,
|
||||
}
|
||||
|
||||
impl fmt::Display for DefId {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "DefId:{}", self.idx)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct DefIdStore {
|
||||
last_idx: u32
|
||||
}
|
||||
|
||||
impl DefIdStore {
|
||||
pub fn new() -> Self {
|
||||
Self { last_idx: 0 }
|
||||
}
|
||||
|
||||
pub fn fresh(&mut self) -> DefId {
|
||||
let idx = self.last_idx;
|
||||
self.last_idx += 1;
|
||||
DefId { idx }
|
||||
}
|
||||
}
|
||||
|
||||
/// Fully-qualified symbol name
|
||||
#[derive(Debug, Clone, Eq, PartialEq, Hash, PartialOrd, Ord)]
|
||||
pub struct Fqsn {
|
||||
@ -113,6 +142,9 @@ impl<K> NameTable<K> {
|
||||
|
||||
//cf. p. 150 or so of Language Implementation Patterns
|
||||
pub struct SymbolTable {
|
||||
|
||||
def_id_store: DefIdStore,
|
||||
|
||||
/// Used for import resolution.
|
||||
symbol_trie: SymbolTrie,
|
||||
|
||||
@ -131,6 +163,7 @@ pub struct SymbolTable {
|
||||
impl SymbolTable {
|
||||
pub fn new() -> SymbolTable {
|
||||
SymbolTable {
|
||||
def_id_store: DefIdStore::new(),
|
||||
symbol_trie: SymbolTrie::new(),
|
||||
fq_names: NameTable::new(),
|
||||
types: NameTable::new(),
|
||||
@ -148,13 +181,18 @@ impl SymbolTable {
|
||||
if !errs.is_empty() {
|
||||
return Err(errs);
|
||||
}
|
||||
self.resolve_symbol_ids(ast);
|
||||
self.resolve_scopes(ast);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn lookup_symbol(&self, id: &ItemId) -> Option<&Symbol> {
|
||||
self.id_to_symbol.get(id).map(|s| s.as_ref())
|
||||
}
|
||||
|
||||
pub fn lookup_symbol_by_def(&self, def: &DefId) -> Option<&Symbol> {
|
||||
self.id_to_symbol.iter().find(|(_, sym)| sym.def_id == *def)
|
||||
.map(|(_, sym)| sym.as_ref())
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
@ -163,6 +201,7 @@ pub struct Symbol {
|
||||
pub local_name: Rc<String>,
|
||||
fully_qualified_name: Fqsn,
|
||||
pub spec: SymbolSpec,
|
||||
pub def_id: DefId,
|
||||
}
|
||||
|
||||
impl fmt::Display for Symbol {
|
||||
@ -224,6 +263,7 @@ impl SymbolTable {
|
||||
local_name: fqsn.local_name(),
|
||||
fully_qualified_name: fqsn.clone(),
|
||||
spec,
|
||||
def_id: self.def_id_store.fresh(),
|
||||
});
|
||||
self.symbol_trie.insert(&fqsn);
|
||||
self.fqsn_to_symbol.insert(fqsn, symbol.clone());
|
||||
@ -232,8 +272,8 @@ impl SymbolTable {
|
||||
|
||||
/// Walks the AST, matching the ID of an identifier used in some expression to
|
||||
/// the corresponding Symbol.
|
||||
fn resolve_symbol_ids(&mut self, ast: &ast::AST) {
|
||||
let mut resolver = resolver::Resolver::new(self);
|
||||
fn resolve_scopes(&mut self, ast: &ast::AST) {
|
||||
let mut resolver = resolver::ScopeResolver::new(self);
|
||||
resolver.resolve(ast);
|
||||
}
|
||||
|
||||
@ -367,6 +407,7 @@ impl SymbolTable {
|
||||
kind: NameKind::Binding,
|
||||
},
|
||||
)?;
|
||||
println!("Adding Binding symbol: {:?}", fq_binding);
|
||||
self.add_symbol(
|
||||
id,
|
||||
fq_binding,
|
||||
|
@ -6,12 +6,12 @@ use crate::util::ScopeStack;
|
||||
|
||||
type FqsnPrefix = Vec<Scope>;
|
||||
|
||||
pub struct Resolver<'a> {
|
||||
pub struct ScopeResolver<'a> {
|
||||
symbol_table: &'a mut super::SymbolTable,
|
||||
name_scope_stack: ScopeStack<'a, Rc<String>, FqsnPrefix>,
|
||||
}
|
||||
|
||||
impl<'a> Resolver<'a> {
|
||||
impl<'a> ScopeResolver<'a> {
|
||||
pub fn new(symbol_table: &'a mut SymbolTable) -> Self {
|
||||
let name_scope_stack: ScopeStack<'a, Rc<String>, FqsnPrefix> = ScopeStack::new(None);
|
||||
Self {
|
||||
@ -47,7 +47,9 @@ impl<'a> Resolver<'a> {
|
||||
}
|
||||
|
||||
fn qualified_name(&mut self, name: &QualifiedName) {
|
||||
println!("Handling qualified_name in resolver.rs: {:?}", name);
|
||||
let fqsn = self.lookup_name_in_scope(name);
|
||||
println!("Computed FQSN: {:?}", fqsn);
|
||||
let symbol = self.symbol_table.fqsn_to_symbol.get(&fqsn);
|
||||
if let Some(symbol) = symbol {
|
||||
self.symbol_table.id_to_symbol.insert(name.id.clone(), symbol.clone());
|
||||
@ -55,7 +57,7 @@ impl<'a> Resolver<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> ASTVisitor for Resolver<'a> {
|
||||
impl<'a> ASTVisitor for ScopeResolver<'a> {
|
||||
//TODO need to un-insert these - maybe need to rethink visitor
|
||||
fn import(&mut self, import_spec: &ImportSpecifier) {
|
||||
let ImportSpecifier {
|
||||
|
144
schala-lang/language/src/tree_walk_eval/mod.rs
Normal file
144
schala-lang/language/src/tree_walk_eval/mod.rs
Normal file
@ -0,0 +1,144 @@
|
||||
use crate::reduced_ir::{ReducedIR, Expression, Function, Statement, Literal};
|
||||
use crate::symbol_table::{DefId};
|
||||
use crate::util::ScopeStack;
|
||||
|
||||
use std::fmt::Write;
|
||||
use std::convert::From;
|
||||
|
||||
type EvalResult<T> = Result<T, String>;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct State<'a> {
|
||||
environments: ScopeStack<'a, DefId, RuntimeValue>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct RuntimeError {
|
||||
msg: String
|
||||
}
|
||||
|
||||
impl From<String> for RuntimeError {
|
||||
fn from(msg: String) -> Self {
|
||||
Self {
|
||||
msg
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl RuntimeError {
|
||||
fn get_msg(&self) -> String {
|
||||
format!("Runtime error: {}", self.msg)
|
||||
}
|
||||
}
|
||||
|
||||
fn paren_wrapped(terms: impl Iterator<Item=String>) -> String {
|
||||
let mut buf = String::new();
|
||||
write!(buf, "(").unwrap();
|
||||
for term in terms.map(Some).intersperse(None) {
|
||||
match term {
|
||||
Some(e) => write!(buf, "{}", e).unwrap(),
|
||||
None => write!(buf, ", ").unwrap(),
|
||||
};
|
||||
}
|
||||
write!(buf, ")").unwrap();
|
||||
buf
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
enum RuntimeValue {
|
||||
Expression(Expression),
|
||||
}
|
||||
|
||||
impl From<Expression> for RuntimeValue {
|
||||
fn from(ex: Expression) -> Self {
|
||||
Self::Expression(ex)
|
||||
}
|
||||
}
|
||||
|
||||
fn expr_to_repl(expr: &Expression) -> String {
|
||||
match expr {
|
||||
Expression::Unimplemented => format!("Expression {:?} not implemented", expr),
|
||||
Expression::Literal(lit) => match lit {
|
||||
Literal::Nat(n) => format!("{}", n),
|
||||
Literal::Int(i) => format!("{}", i),
|
||||
Literal::Float(f) => format!("{}", f),
|
||||
Literal::Bool(b) => format!("{}", b),
|
||||
Literal::StringLit(s) => format!("\"{}\"", s),
|
||||
}
|
||||
Expression::Tuple(terms) => paren_wrapped(terms.iter().map(|x| expr_to_repl(x))),
|
||||
Expression::Assign { lval, rval } => {
|
||||
"".to_string()
|
||||
},
|
||||
e => format!("Expression {:?} shouldn't be here", e),
|
||||
}
|
||||
}
|
||||
|
||||
impl RuntimeValue {
|
||||
fn to_repl(&self) -> String {
|
||||
match self {
|
||||
RuntimeValue::Expression(ref expr) => expr_to_repl(expr)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> State<'a> {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
environments: ScopeStack::new(Some("global".to_string()))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn evaluate(&mut self, reduced: ReducedIR, repl: bool) -> Vec<Result<String, String>> {
|
||||
let mut acc = vec![];
|
||||
|
||||
for statement in reduced.entrypoint.into_iter() {
|
||||
match self.statement(statement) {
|
||||
Ok(Some(output)) if repl => {
|
||||
acc.push(Ok(output.to_repl()))
|
||||
},
|
||||
Ok(_) => (),
|
||||
Err(error) => {
|
||||
acc.push(Err(error.into()));
|
||||
return acc;
|
||||
}
|
||||
}
|
||||
}
|
||||
acc
|
||||
}
|
||||
|
||||
fn statement(&mut self, stmt: Statement) -> EvalResult<Option<RuntimeValue>> {
|
||||
match stmt {
|
||||
Statement::Binding { id, expr, constant } => {
|
||||
println!("eval() binding id: {}", id);
|
||||
let evaluated = self.expression(expr)?;
|
||||
self.environments.insert(id, evaluated.into());
|
||||
Ok(None)
|
||||
},
|
||||
Statement::Expression(expr) => {
|
||||
let evaluated = self.expression(expr)?;
|
||||
Ok(Some(evaluated.into()))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn expression(&mut self, expression: Expression) -> EvalResult<Expression> {
|
||||
use Expression::Unimplemented;
|
||||
Ok(match expression {
|
||||
lit @ Expression::Literal(_) => lit,
|
||||
Expression::Tuple(items) => Expression::Tuple(items.into_iter().map(|expr| self.expression(expr)).collect::<EvalResult<Vec<Expression>>>()?),
|
||||
Expression::Assign { lval, box rval } => {
|
||||
let mut env = self.environments.lookup(&lval);
|
||||
Unimplemented
|
||||
},
|
||||
Expression::Call { f, args } => self.call_expression(f, args)?,
|
||||
Unimplemented => Unimplemented,
|
||||
Expression::ReductionError(e) => return Err(e.into()),
|
||||
_ => Expression::Literal(Literal::Nat(69420)),
|
||||
})
|
||||
}
|
||||
|
||||
fn call_expression(&mut self, f: Function, args: Vec<Expression>) -> EvalResult<Expression> {
|
||||
Err("Call expression not implemented".to_string().into())
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user