Skip to content

Commit

Permalink
Add stuff
Browse files Browse the repository at this point in the history
  • Loading branch information
ryzokuken committed Dec 14, 2019
1 parent e22c585 commit 0278845
Show file tree
Hide file tree
Showing 10 changed files with 316 additions and 16 deletions.
47 changes: 47 additions & 0 deletions src/expression.rs
@@ -0,0 +1,47 @@
// A recursive descent parser operates by defining functions for all
// syntactic elements, and recursively calling those, each function
// advancing the input stream and returning an AST node. Precedence
// of constructs (for example, the fact that `!x[1]` means `!(x[1])`
// instead of `(!x)[1]` is handled by the fact that the parser
// function that parses unary prefix operators is called first, and
// in turn calls the function that parses `[]` subscripts — that
// way, it'll receive the node for `x[1]` already parsed, and wraps
// *that* in the unary operator node.
//
// Acorn uses an [operator precedence parser][opp] to handle binary
// operator precedence, because it is much more compact than using
// the technique outlined above, which uses different, nesting
// functions to specify precedence, for all of the ten binary
// precedence levels that JavaScript defines.
//
// [opp]: http://en.wikipedia.org/wiki/Operator-precedence_parser

use crate::node;
use crate::parseutil;
use crate::state;
use crate::tokentype;

pub trait ParserExpression {
fn parseExpression(
self,
noIn: Option<bool>,
refDestructuringErrors: Option<parseutil::DestructuringErrors>,
) -> node::Node;
}

impl ParserExpression for state::Parser {
fn parseExpression(
self,
noIn: Option<bool>,
refDestructuringErrors: Option<parseutil::DestructuringErrors>,
) -> node::Node {
let startPos = self.pos;
let startLoc = self.startLoc;
let expr = self.maybeAssign(noIn, refDestructuringErrors);
if self.r#type == tokentype::TokenType::comma() {
let node = self.startNodeAt(startPos, startLoc);
// TODO: finish this
}
expr
}
}
6 changes: 5 additions & 1 deletion src/lib.rs
@@ -1,13 +1,17 @@
pub mod expression;
pub mod location;
pub mod locutil;
pub mod node;
pub mod options;
pub mod parseutil;
pub mod state;
pub mod statement;
pub mod tokencontext;
pub mod tokenize;
pub mod tokentype;
pub mod whitespace;

pub fn parse(input: String, options: Option<options::Options>) -> node::Node {
// node::Node {}
state::Parser::parse(input, options)
}

Expand Down
18 changes: 18 additions & 0 deletions src/location.rs
@@ -0,0 +1,18 @@
use crate::locutil;
use crate::state;

trait ParserLocation {
fn curPosition(self) -> Option<locutil::Position>;
}

impl ParserLocation for state::Parser {
fn curPosition(self) -> Option<locutil::Position> {
if self.options.locations {
return Some(locutil::Position::new(
self.curLine,
self.pos - self.lineStart,
));
}
None
}
}
14 changes: 9 additions & 5 deletions src/locutil.rs
@@ -1,23 +1,23 @@
use crate::state;

pub struct Position {
line: u32,
column: u32,
line: usize,
column: usize,
}

impl Position {
fn new(line: u32, column: u32) -> Self {
pub fn new(line: usize, column: usize) -> Self {
Position { line, column }
}

fn offset(&self, n: u32) -> Self {
fn offset(&self, n: usize) -> Self {
Position::new(self.line, self.column + n)
}
}

pub struct SourceLocation {
start: Position,
end: Position,
pub end: Position,
source: String,
}

Expand All @@ -33,4 +33,8 @@ impl SourceLocation {
}
loc
}

pub fn from_parser(p: state::Parser) -> Self {
SourceLocation::new(p, p.startLoc.unwrap(), p.endLoc.unwrap())
}
}
53 changes: 49 additions & 4 deletions src/node.rs
@@ -1,24 +1,31 @@
use crate::locutil;
use crate::options;
use crate::state;

pub struct Node {
r#type: String,
start: u32,
end: u32,
pub start: usize,
end: usize,
loc: Option<locutil::SourceLocation>,
sourceFile: Option<String>,
range: Option<(u32, u32)>,
range: Option<(usize, usize)>,
pub body: Option<Vec<Node>>,
pub local: Option<Box<Node>>,
pub sourceType: Option<options::SourceType>,
}

impl Node {
fn new(parser: state::Parser, pos: u32, loc: Option<locutil::SourceLocation>) -> Node {
fn new(parser: state::Parser, pos: usize, loc: Option<locutil::SourceLocation>) -> Self {
let node = Node {
r#type: String::from(""),
start: pos,
end: 0,
loc: None,
sourceFile: None,
range: None,
body: None,
local: None,
sourceType: None,
};
if parser.options.locations {
// TODO(ryzokuken): Talk to acorn maintainers about this.
Expand All @@ -28,3 +35,41 @@ impl Node {
node
}
}

pub trait ParserNode {
fn startNode(self) -> Node;
fn finishNode(self, node: Node, r#type: String) -> Node;
}

impl ParserNode for state::Parser {
fn startNode(self) -> Node {
Node::new(
self,
self.start,
Some(locutil::SourceLocation::from_parser(self)),
)
}

fn finishNode(self, node: Node, r#type: String) -> Node {
finishNodeAt(self, node, r#type, self.lastTokEnd, self.lastTokEndLoc)
}
}

// Finish an AST node, adding `type` and `end` properties.
fn finishNodeAt(
parser: state::Parser,
node: Node,
r#type: String,
pos: usize,
loc: Option<locutil::Position>,
) -> Node {
node.r#type = r#type;
node.end = pos;
if parser.options.locations {
node.loc.unwrap().end = loc.unwrap();
}
if parser.options.ranges {
node.range.unwrap().1 = pos;
}
node
}
12 changes: 7 additions & 5 deletions src/options.rs
@@ -1,7 +1,8 @@
use crate::locutil;
use crate::node;

enum EcmaVersion {
#[derive(PartialEq, PartialOrd)]
pub enum EcmaVersion {
Ecma3,
Ecma5,
Ecma6,
Expand All @@ -22,7 +23,8 @@ impl Default for EcmaVersion {
}
}

enum SourceType {
#[derive(PartialEq)]
pub enum SourceType {
Script,
Module,
}
Expand All @@ -37,8 +39,8 @@ impl Default for SourceType {
// TODO(ryzokuken): onComment
#[derive(Default)]
pub struct Options {
ecmaVersion: EcmaVersion,
sourceType: SourceType,
pub ecmaVersion: EcmaVersion,
pub sourceType: SourceType,
onInsertedSemicolon: Option<fn(u32, Option<locutil::Position>) -> ()>,
onTrailingComma: Option<fn(u32, Option<locutil::Position>) -> ()>,
allowReserved: Option<bool>,
Expand All @@ -47,7 +49,7 @@ pub struct Options {
allowAwaitOutsideFunction: bool,
allowHashBang: bool,
pub locations: bool,
ranges: bool,
pub ranges: bool,
pub program: Option<node::Node>,
pub sourceFile: Option<String>,
directSourceFile: Option<String>,
Expand Down
19 changes: 19 additions & 0 deletions src/parseutil.rs
@@ -0,0 +1,19 @@
pub struct DestructuringErrors {
shorthandAssign: isize,
trailingComma: isize,
parenthesizedAssign: isize,
parenthesizedBind: isize,
doubleProto: isize,
}

impl DestructuringErrors {
fn new() -> Self {
DestructuringErrors {
shorthandAssign: -1,
trailingComma: -1,
parenthesizedAssign: -1,
parenthesizedBind: -1,
doubleProto: -1,
}
}
}
39 changes: 38 additions & 1 deletion src/state.rs
@@ -1,9 +1,18 @@
use crate::expression;
use crate::locutil;
use crate::node;
use crate::options;
use crate::statement;
use crate::tokencontext;
use crate::tokenize;
use crate::tokentype;
use crate::whitespace;

use std::collections::HashMap;

use expression::ParserExpression;
use node::ParserNode;
use statement::ParserStatement;
use tokenize::ParserTokenize;

pub struct Parser {
Expand All @@ -20,6 +29,18 @@ pub struct Parser {
pub curLine: usize,

pub context: Vec<tokencontext::TokContext>,

pub r#type: tokentype::TokenType,
pub undefinedExports: HashMap<String, node::Node>,
pub inModule: bool,
pub lastTokStart: usize,
pub lastTokEnd: usize,
pub lastTokStartLoc: Option<locutil::Position>,
pub lastTokEndLoc: Option<locutil::Position>,
pub start: usize,
pub end: usize,
pub startLoc: Option<locutil::Position>,
pub endLoc: Option<locutil::Position>,
}

// TODO(ryzokuken): do you need sourceFile?
Expand All @@ -33,6 +54,17 @@ impl Parser {
lineStart: 0,
curLine: 1,
context: vec![tokencontext::TokContext::b_stat()],
r#type: tokentype::TokenType::eof(),
undefinedExports: HashMap::new(),
inModule: false,
lastTokStart: 0,
lastTokEnd: 0,
lastTokStartLoc: None,
lastTokEndLoc: None,
start: 0,
end: 0,
startLoc: None,
endLoc: None,
};
if startPos.is_some() {
let pos = startPos.unwrap();
Expand All @@ -43,6 +75,11 @@ impl Parser {
.collect();
parser.curLine = cline.len();
}
parser.start = parser.pos;
parser.end = parser.pos;
parser.lastTokStart = parser.pos;
parser.lastTokEnd = parser.pos;
parser.inModule = options.sourceType != options::SourceType::Module;
parser
}

Expand All @@ -64,6 +101,6 @@ impl Parser {
) -> node::Node {
let parser = Parser::new(options.unwrap_or_default(), input, Some(pos));
parser.nextToken();
parser.parseExpression()
parser.parseExpression(None, None)
}
}
46 changes: 46 additions & 0 deletions src/statement.rs
@@ -0,0 +1,46 @@
use crate::node;
use crate::options;
use crate::state;
use crate::tokentype;

use std::collections::HashMap;

use node::ParserNode;

// ### Statement parsing

// Parse a program. Initializes the parser, reads any number of
// statements, and wraps them in a Program node. Optionally takes a
// `program` argument. If present, the statements will be appended
// to its body instead of creating a new node.

pub trait ParserStatement {
fn parseTopLevel(&self, node: node::Node) -> node::Node;
}

impl ParserStatement for state::Parser {
fn parseTopLevel(&self, node: node::Node) -> node::Node {
let exports: HashMap<String, bool> = HashMap::new();
if node.body.is_none() {
node.body = Some(Vec::new());
}
while self.r#type != tokentype::TokenType::eof() {
let stmt = self.parseStatement(None, true, exports);
node.body.expect("").push(stmt);
}
if self.inModule {
for name in self.undefinedExports.keys() {
self.raiseRecoverable(
self.undefinedExports[name].start,
format!("Export #{} is not defined", name),
);
}
}
self.adaptDirectivePrologue(node.body);
self.next();
if self.options.ecmaVersion >= options::EcmaVersion::Ecma6 {
node.sourceType = Some(self.options.sourceType);
}
self.finishNode(node, String::from("Program"))
}
}

0 comments on commit 0278845

Please sign in to comment.