require explicit variable declarations
This commit is contained in:
parent
cb32a2c7e2
commit
57e74ab52a
5 changed files with 96 additions and 42 deletions
|
@ -97,6 +97,7 @@ impl Parser {
|
|||
token::Kind::SourceKeyword => self.parse_source_stmt(),
|
||||
token::Kind::TargetKeyword => self.parse_target_stmt(),
|
||||
token::Kind::TypeKeyword => self.parse_type_stmt(),
|
||||
token::Kind::VarKeyword => self.parse_decl_stmt(),
|
||||
token::Kind::WhileKeyword => self.parse_while_stmt(),
|
||||
k if k.is_start_of_expr() => self.parse_expr_stmt(),
|
||||
_ => self.syntax_error(format!("Unexpected token {}", token), &token),
|
||||
|
@ -147,6 +148,23 @@ impl Parser {
|
|||
Ok(Node::ReturnStmt(return_keyword.pos, Box::new(expr)))
|
||||
}
|
||||
|
||||
/// ```notrust
|
||||
/// DeclarationStatement
|
||||
/// : "var" Identifier [ "=" Expression ] ";"
|
||||
/// ```
|
||||
fn parse_decl_stmt(&mut self) -> Result<Node, Error> {
|
||||
let var_keyword = self.lexer.expect_kind(token::Kind::VarKeyword)?;
|
||||
let ident = self.lexer.expect_kind(token::Kind::Ident)?;
|
||||
let ident = Node::Ident(ident.pos, ident.raw);
|
||||
let initializer = if self.lexer.next_if(token::Kind::Eq).is_some() {
|
||||
Some(self.parse_expr(&[token::Kind::Semi])?)
|
||||
} else {
|
||||
self.lexer.expect_kind(token::Kind::Semi)?;
|
||||
None
|
||||
};
|
||||
Ok(Node::make_decl_stmt(var_keyword.pos, ident, initializer))
|
||||
}
|
||||
|
||||
/// ```notrust
|
||||
/// TargetStatement
|
||||
/// : "target" Expression BlockStatement
|
||||
|
@ -163,11 +181,7 @@ impl Parser {
|
|||
let children = self.parse_block_stmt()?;
|
||||
|
||||
self.scope.pop();
|
||||
Ok(Node::make_target_stmt(
|
||||
target_keyword.pos,
|
||||
name,
|
||||
children,
|
||||
))
|
||||
Ok(Node::make_target_stmt(target_keyword.pos, name, children))
|
||||
}
|
||||
|
||||
/// ```notrust
|
||||
|
@ -216,11 +230,7 @@ impl Parser {
|
|||
let condition = self.parse_expr(&[token::Kind::CParen])?;
|
||||
let body = self.parse_block_stmt()?;
|
||||
self.scope.pop();
|
||||
Ok(Node::make_while_stmt(
|
||||
while_keyword.pos,
|
||||
condition,
|
||||
body,
|
||||
))
|
||||
Ok(Node::make_while_stmt(while_keyword.pos, condition, body))
|
||||
}
|
||||
|
||||
/// ```notrust
|
||||
|
@ -265,11 +275,7 @@ impl Parser {
|
|||
match expr {
|
||||
Node::BinaryExpr(_, expr) => {
|
||||
if expr.op == Operator::Eq {
|
||||
Ok(Node::make_set_stmt(
|
||||
set_keyword.pos,
|
||||
*expr.lhs,
|
||||
*expr.rhs,
|
||||
))
|
||||
Ok(Node::make_set_stmt(set_keyword.pos, *expr.lhs, *expr.rhs))
|
||||
} else {
|
||||
self.syntax_error(format!("Invalid operator"), self.lexer.current().unwrap())
|
||||
}
|
||||
|
@ -393,10 +399,7 @@ impl Parser {
|
|||
/// : "||" | "&&" | "==" | "!=" | "<" | "<=" | ">" | ">="
|
||||
/// : "|" | "^" | "&" | "<<" | ">>" | "+" | "-" | "*" | "/" | "%"
|
||||
/// ```
|
||||
fn parse_binary_expr_or_higher(
|
||||
&mut self,
|
||||
terminators: &[token::Kind],
|
||||
) -> Result<Node, Error> {
|
||||
fn parse_binary_expr_or_higher(&mut self, terminators: &[token::Kind]) -> Result<Node, Error> {
|
||||
let mut lhs = self.parse_unary_expr_or_higher()?;
|
||||
|
||||
while let Some(Ok(token)) = self.lexer.peek() {
|
||||
|
@ -569,17 +572,13 @@ impl Parser {
|
|||
self.lexer.next();
|
||||
let params =
|
||||
self.parse_delimited_list(token::Kind::Comma, token::Kind::CParen, false)?;
|
||||
self.parse_primary_expr_rest(Node::make_call_expr(
|
||||
token.pos, start, params,
|
||||
))
|
||||
self.parse_primary_expr_rest(Node::make_call_expr(token.pos, start, params))
|
||||
}
|
||||
token::Kind::OBracket => {
|
||||
// array index
|
||||
self.lexer.next();
|
||||
let index = self.parse_expr(&[token::Kind::CBracket])?;
|
||||
self.parse_primary_expr_rest(Node::make_array_expr(
|
||||
token.pos, start, index,
|
||||
))
|
||||
self.parse_primary_expr_rest(Node::make_array_expr(token.pos, start, index))
|
||||
}
|
||||
token::Kind::Dot => {
|
||||
// member access
|
||||
|
|
|
@ -23,6 +23,7 @@ pub enum Node {
|
|||
Fn(Position, Arc<FnStmt>),
|
||||
ArrayExpr(Position, ArrayExpr),
|
||||
CallExpr(Position, CallExpr),
|
||||
DeclStmt(Position, DeclStmt),
|
||||
IfStmt(Position, IfStmt),
|
||||
ReturnStmt(Position, Box<Node>),
|
||||
ForStmt(Position, ForStmt),
|
||||
|
@ -40,6 +41,11 @@ pub struct File {
|
|||
pub content: Vec<Node>,
|
||||
}
|
||||
|
||||
pub struct DeclStmt {
|
||||
pub name: Box<Node>,
|
||||
pub val: Option<Box<Node>>,
|
||||
}
|
||||
|
||||
pub struct FnStmt {
|
||||
pub name: Option<Box<Node>>,
|
||||
pub params: Vec<Node>,
|
||||
|
@ -170,6 +176,7 @@ impl Node {
|
|||
Node::IfStmt(pos, _) => pos,
|
||||
Node::ForStmt(pos, _) => pos,
|
||||
Node::WhileStmt(pos, _) => pos,
|
||||
Node::DeclStmt(pos, _) => pos,
|
||||
Node::UnaryExpr(pos, _) => pos,
|
||||
Node::BinaryExpr(pos, _) => pos,
|
||||
Node::TypeStmt(pos, _) => pos,
|
||||
|
@ -241,6 +248,12 @@ impl Node {
|
|||
stmt.body.visit(cb, depth);
|
||||
}
|
||||
Node::ReturnStmt(_, node) => node.visit(cb, depth),
|
||||
Node::DeclStmt(_, stmt) => {
|
||||
stmt.name.visit(cb, depth);
|
||||
if let Some(val) = &stmt.val {
|
||||
val.visit(cb, depth);
|
||||
}
|
||||
}
|
||||
Node::UnaryExpr(_, expr) => expr.rhs.visit(cb, depth),
|
||||
Node::BinaryExpr(_, expr) => {
|
||||
expr.lhs.visit(cb, depth);
|
||||
|
@ -321,6 +334,21 @@ impl Node {
|
|||
)
|
||||
}
|
||||
|
||||
pub fn make_decl_stmt(pos: Position, name: Node, val: Option<Node>) -> Node {
|
||||
let val = if let Some(val) = val {
|
||||
Some(Box::new(val))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
Node::DeclStmt(
|
||||
pos,
|
||||
DeclStmt {
|
||||
name: Box::new(name),
|
||||
val,
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
pub fn make_set_stmt(pos: Position, name: Node, val: Node) -> Node {
|
||||
Node::SetStmt(
|
||||
pos,
|
||||
|
@ -435,6 +463,7 @@ impl fmt::Display for Node {
|
|||
Node::IfStmt(_, _) => "<if>",
|
||||
Node::ForStmt(_, _) => "<for>",
|
||||
Node::WhileStmt(_, _) => "<while>",
|
||||
Node::DeclStmt(_, _) => "<decl>",
|
||||
Node::UnaryExpr(_, expr) => expr.op.raw(),
|
||||
Node::BinaryExpr(_, expr) => expr.op.raw(),
|
||||
Node::TypeStmt(_, _) => "type",
|
||||
|
|
|
@ -35,7 +35,7 @@ const fn kw(raw: &'static str, kind: token::Kind) -> KeywordMap {
|
|||
KeywordMap { raw, kind }
|
||||
}
|
||||
|
||||
static KEYWORDS: [KeywordMap; 15] = [
|
||||
static KEYWORDS: [KeywordMap; 16] = [
|
||||
kw("break", token::Kind::BreakKeyword),
|
||||
kw("depend", token::Kind::DependKeyword),
|
||||
kw("else", token::Kind::ElseKeyword),
|
||||
|
@ -50,6 +50,7 @@ static KEYWORDS: [KeywordMap; 15] = [
|
|||
kw("target", token::Kind::TargetKeyword),
|
||||
kw("true", token::Kind::TrueKeyword),
|
||||
kw("type", token::Kind::TypeKeyword),
|
||||
kw("var", token::Kind::VarKeyword),
|
||||
kw("while", token::Kind::WhileKeyword),
|
||||
];
|
||||
|
||||
|
|
|
@ -83,6 +83,7 @@ pub enum Kind {
|
|||
TargetKeyword,
|
||||
TrueKeyword,
|
||||
TypeKeyword,
|
||||
VarKeyword,
|
||||
WhileKeyword,
|
||||
|
||||
StringLiteral,
|
||||
|
@ -234,6 +235,7 @@ impl fmt::Display for Kind {
|
|||
Kind::TargetKeyword => "keyword",
|
||||
Kind::TrueKeyword => "keyword",
|
||||
Kind::TypeKeyword => "keyword",
|
||||
Kind::VarKeyword => "keyword",
|
||||
Kind::WhileKeyword => "keyword",
|
||||
|
||||
Kind::StringLiteral => "string",
|
||||
|
|
|
@ -11,16 +11,11 @@ use val::{Scope, Val, ValOps};
|
|||
|
||||
/// Execute a program.
|
||||
/// The `root` node must be a `Node::File`.
|
||||
/// This is just a wrapper for `exec_file()` that removes all the
|
||||
/// obvious boilerplate we don't need.
|
||||
pub fn exec(root: &Node) -> Result<Val, Error> {
|
||||
match root {
|
||||
Node::File(pos, f) => {
|
||||
let mut root_scope = Scope::new_root(pos.file.clone());
|
||||
let mut val = Val::new(());
|
||||
for node in &f.content {
|
||||
val = node.eval(&mut root_scope)?;
|
||||
}
|
||||
Ok(val)
|
||||
}
|
||||
Node::File(pos, f) => exec_file(pos.file.clone(), &f.content),
|
||||
node => Err(Error::runtime_error(
|
||||
empty_pos(),
|
||||
format!("cannot exec non-file node {}", node),
|
||||
|
@ -28,6 +23,16 @@ pub fn exec(root: &Node) -> Result<Val, Error> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Actually execute a program.
|
||||
fn exec_file(filename: String, content: &Vec<Node>) -> Result<Val, Error> {
|
||||
let mut root_scope = Scope::new_root(filename);
|
||||
let mut val = Val::new(());
|
||||
for node in content {
|
||||
val = node.eval(&mut root_scope)?;
|
||||
}
|
||||
Ok(val)
|
||||
}
|
||||
|
||||
impl Node {
|
||||
fn eval(&self, scope: &mut Scope) -> Result<Val, Error> {
|
||||
match self {
|
||||
|
@ -88,15 +93,26 @@ impl Node {
|
|||
},
|
||||
&stmt.body,
|
||||
),
|
||||
Node::DeclStmt(_, stmt) => {
|
||||
let name = stmt.name.assert_ident()?;
|
||||
let val = if let Some(val) = &stmt.val {
|
||||
val.eval(scope)?
|
||||
} else {
|
||||
Val::new(())
|
||||
};
|
||||
scope.define(name.clone(), val);
|
||||
Ok(Val::new(()))
|
||||
}
|
||||
Node::CallExpr(_, expr) => {
|
||||
// we don't execute the function within the callback because
|
||||
// (1) that would block the mutex for way longer than necessary and
|
||||
// (2) recursive function calls would cause a deadlock (ask me how i know)
|
||||
let (func, mut capture) = expr.func.eval(scope)?.as_fn(|func, capture| {
|
||||
(func.clone(), capture.clone())
|
||||
})?;
|
||||
let (func, mut capture) = expr
|
||||
.func
|
||||
.eval(scope)?
|
||||
.as_fn(|func, capture| (func.clone(), capture.clone()))?;
|
||||
exec_fn(scope, &mut capture, &func.body, &func.params, &expr.params)
|
||||
},
|
||||
}
|
||||
Node::ArrayExpr(pos, expr) => expr.array.eval(scope)?.as_array(|array| {
|
||||
expr.index.eval(scope)?.as_int(|index| {
|
||||
if *index >= 0 && *index < array.len() as i128 {
|
||||
|
@ -177,14 +193,21 @@ impl Operator {
|
|||
match self {
|
||||
Operator::Eq => match lhs {
|
||||
Node::Ident(pos, name) => {
|
||||
let val = rhs.eval(scope)?;
|
||||
match val.typ() {
|
||||
let new_val = rhs.eval(scope)?;
|
||||
match new_val.typ() {
|
||||
Type::Void => {
|
||||
Err(Error::type_error(pos.clone(), Type::Unknown, Type::Void))
|
||||
}
|
||||
_ => {
|
||||
scope.define(name.clone(), val);
|
||||
Ok(Val::new(()))
|
||||
if let Some(val) = scope.resolve(name) {
|
||||
val.update(&new_val);
|
||||
Ok(Val::new(()))
|
||||
} else {
|
||||
Err(Error::runtime_error(
|
||||
pos.clone(),
|
||||
format!("Identifier \"{}\" not found in this scope", name),
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue