Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add more code #7

Merged
merged 2 commits into from Dec 14, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Binary file added docs/TERM PAPER FINAL.pdf
Binary file not shown.
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
}
}
5 changes: 4 additions & 1 deletion src/lib.rs
@@ -1,14 +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
}
10 changes: 6 additions & 4 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 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,
}
}
}
41 changes: 39 additions & 2 deletions 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;

// TODO(ryzokuken): Figure this out.
Expand All @@ -27,6 +36,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 @@ -40,6 +61,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 @@ -50,6 +82,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 @@ -71,10 +108,10 @@ impl Parser {
) -> node::Node {
let parser = Parser::new(options.unwrap_or_default(), input, Some(pos));
parser.nextToken();
parser.parseExpression()
parser.parseExpression(None, None)
}

pub fn tokenizer(input: String, options: Option<options::Options>) -> Parser {
Box::from(Parser::new(options.unwrap_or_default(), input, None))
Parser::new(options.unwrap_or_default(), input, 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"))
}
}
2 changes: 1 addition & 1 deletion src/tokenize.rs
Expand Up @@ -25,7 +25,7 @@ impl Token {
range: None,
};
if p.options.locations {
token.loc = Some(locutil::SourceLocation::new(p, p.startLoc, p.endLoc));
token.loc = Some(locutil::SourceLocation::from_parser(p));
};
if p.options.ranges {
token.range = Some((p.start, p.end));
Expand Down