add functions

main
anna 2 years ago
parent 6d5e7cb406
commit 620be5dba1
Signed by: fef
GPG Key ID: EC22E476DC2D3D84

@ -15,6 +15,7 @@ enum Scope {
Target,
DepList,
SourceList,
Function,
}
struct Parser {
@ -79,12 +80,14 @@ impl Parser {
let token = self.lexer.peek_or_err()?;
match token.kind {
token::Kind::DependKeyword => self.parse_depend_stmt(),
token::Kind::FnKeyword => self.parse_fn(false),
token::Kind::IfKeyword => self.parse_if_stmt(),
token::Kind::ReturnKeyword => self.parse_return_stmt(),
token::Kind::SetKeyword => self.parse_set_stmt(),
token::Kind::SourceKeyword => self.parse_source_stmt(),
token::Kind::TargetKeyword => self.parse_target_stmt(),
token::Kind::TypeKeyword => self.parse_type_stmt(),
token::Kind::Ident => self.parse_expr_stmt(),
k if k.is_start_of_expr() => self.parse_expr_stmt(),
_ => self.syntax_error(format!("Unexpected token {}", token), &token),
}
}
@ -111,6 +114,17 @@ impl Parser {
Ok(tree::Node::Block(nodes))
}
/// ```notrust
/// ReturnStatement
/// : "return" Expression ";"
/// ```
fn parse_return_stmt(&mut self) -> Result<tree::Node, Error> {
self.assert_scope(Scope::Function)?;
self.lexer.expect_kind(token::Kind::ReturnKeyword)?;
let expr = self.parse_expr(&[token::Kind::Semi])?;
Ok(tree::Node::ReturnStmt(Box::new(expr)))
}
/// ```notrust
/// TargetStatement
/// : "target" Expression BlockStatement
@ -452,6 +466,10 @@ impl Parser {
token::Kind::StringLiteral => Ok(tree::Node::String(token.raw)),
token::Kind::TrueKeyword => Ok(tree::Node::Bool(true)),
token::Kind::FalseKeyword => Ok(tree::Node::Bool(false)),
token::Kind::FnKeyword => {
self.lexer.prev(); // parse_fn() expects to consume the keyword
self.parse_fn(true)
}
token::Kind::OBracket => {
let elements =
self.parse_delimited_list(token::Kind::Comma, token::Kind::CBracket, true)?;
@ -469,6 +487,7 @@ impl Parser {
/// matrix[y][x]
/// array_of_functions[index](params)
/// function_returning_an_array(params)[index]
/// (fn(a, b) { return a + b; })(1, 2)
/// ```
fn parse_primary_expr_rest(&mut self, start: tree::Node) -> Result<tree::Node, Error> {
if let Some(Ok(token)) = self.lexer.peek() {
@ -499,6 +518,56 @@ impl Parser {
}
}
/// ```notrust
/// Function
/// : "fn" [ Identifier ] "(" [ ParameterList ] ")" BlockStatement
///
/// ParameterList
/// : Identifier [ "," ParameterList ]
/// ```
fn parse_fn(&mut self, allow_anonymous: bool) -> Result<tree::Node, Error> {
self.scope.push(Scope::Function);
self.lexer.expect_kind(token::Kind::FnKeyword)?;
// function name is optional (there are inline anonymous functions)
let name = if let Some(Ok(token)) = self.lexer.peek() {
if token.kind == token::Kind::Ident {
self.lexer.next();
Some(Box::new(tree::Node::Ident(token.raw)))
} else {
None
}
} else {
None
};
let oparen = self.lexer.expect_kind(token::Kind::OParen)?;
if name.is_none() && !allow_anonymous {
// anonymous function are not allowed for definitions as a block
// statement (you can only do that with inline functions)
return self.syntax_error(String::from("Function name required"), &oparen);
}
let params = self.parse_delimited_list(token::Kind::Comma, token::Kind::CParen, false)?;
for p in &params {
match p {
tree::Node::Ident(_) => continue,
_ => {
return self
.syntax_error(format!("Not an identifier"), &self.lexer.current().unwrap())
}
}
}
let body = self.parse_block_stmt()?;
self.scope.pop();
Ok(tree::Node::Fn {
name,
params,
body: Box::new(body),
})
}
/// Parse a terminated, delimited list of expressions. This is used for
/// parameter lists in function calls and elements in array literals.
fn parse_delimited_list(

@ -16,6 +16,11 @@ pub enum Node {
String(String),
Array(Vec<Node>),
Block(Vec<Node>),
Fn {
name: Option<Box<Node>>,
params: Vec<Node>,
body: Box<Node>,
},
ArrayExpr {
// array access
array: Box<Node>,
@ -31,6 +36,7 @@ pub enum Node {
then_block: Box<Node>,
else_block: Option<Box<Node>>,
},
ReturnStmt(Box<Node>),
UnaryExpr {
op: Operator,
node: Box<Node>,
@ -110,6 +116,15 @@ impl Node {
node.visit(cb, depth);
}
}
Node::Fn { name, params, body } => {
if let Some(n) = name {
n.visit(cb, depth);
}
for p in params {
p.visit(cb, depth);
}
body.visit(cb, depth);
}
Node::ArrayExpr { array, index } => {
array.visit(cb, depth);
index.visit(cb, depth);
@ -131,6 +146,7 @@ impl Node {
eb.visit(cb, depth);
}
}
Node::ReturnStmt(node) => node.visit(cb, depth),
Node::UnaryExpr { op, node } => node.visit(cb, depth),
Node::BinaryExpr { op, lhs, rhs } => {
lhs.visit(cb, depth);
@ -173,6 +189,8 @@ impl fmt::Display for Node {
}
Node::String(s) => s.as_str(),
Node::Block(_) => "<block>",
Node::Fn { name, params, body } => "fn",
Node::ReturnStmt(_) => "return",
Node::DepList(_) => "depend",
Node::SourceList(_) => "source",
Node::Array(_) => "<array>",

@ -35,12 +35,14 @@ const fn kw(raw: &'static str, kind: token::Kind) -> KeywordMap {
KeywordMap { raw, kind }
}
static KEYWORDS: [KeywordMap; 10] = [
static KEYWORDS: [KeywordMap; 12] = [
kw("depend", token::Kind::DependKeyword),
kw("else", token::Kind::ElseKeyword),
kw("false", token::Kind::FalseKeyword),
kw("fn", token::Kind::FnKeyword),
kw("if", token::Kind::IfKeyword),
kw("include", token::Kind::IncludeKeyword),
kw("return", token::Kind::ReturnKeyword),
kw("set", token::Kind::SetKeyword),
kw("source", token::Kind::SourceKeyword),
kw("target", token::Kind::TargetKeyword),

@ -71,8 +71,10 @@ pub enum Kind {
DependKeyword,
ElseKeyword,
FalseKeyword,
FnKeyword,
IfKeyword,
IncludeKeyword,
ReturnKeyword,
SetKeyword,
SourceKeyword,
TargetKeyword,
@ -98,9 +100,11 @@ impl Kind {
match self {
k if k.is_start_of_lhs_expr() => true,
Kind::FalseKeyword => true,
Kind::FnKeyword => true,
Kind::Minus => true,
Kind::OBracket => true,
Kind::OParen => true,
Kind::ReturnKeyword => true,
Kind::TrueKeyword => true,
_ => false,
}
@ -214,8 +218,10 @@ impl fmt::Display for Kind {
Kind::DependKeyword => "keyword",
Kind::ElseKeyword => "keyword",
Kind::FalseKeyword => "keyword",
Kind::FnKeyword => "keyword",
Kind::IfKeyword => "keyword",
Kind::IncludeKeyword => "keyword",
Kind::ReturnKeyword => "keyword",
Kind::SetKeyword => "keyword",
Kind::SourceKeyword => "keyword",
Kind::TargetKeyword => "keyword",

@ -6,6 +6,13 @@ set CC_EXE = "clang";
set LINK_EXE = "ld.lld";
set BUILD_PREFIX = "build";
# regular function definitions work very much like they do in ECMAScript 5,
# except that the rather verbose "function" keyword is shortened to "fn".
fn hello(p1, p2) {
print("hello, world");
return p1 + p1;
}
# a target is a single component.
# targets can depend on other targets.
target kern {
@ -15,6 +22,11 @@ target kern {
print("2 <= 3");
}
# functions are first-class citizens; they can be defined as inline
# anonymous functions that you can treat like any other value
fn_with_callback(fn(a, b) { return a + b; });
(fn(a, b) { return a + b; })(1, 2);
owo = 1 + 2 * 3 * 4 - 5;
# the type keyword defines whether the target is supposed to be

Loading…
Cancel
Save