From 27e4d93af741f52f8af59a83a0b2c5e34b89a3ef Mon Sep 17 00:00:00 2001 From: Jason Williams Date: Tue, 28 Aug 2018 17:07:21 +0100 Subject: [PATCH] adding Ops and expressions --- Dockerfile | 10 +- src/lib/syntax/ast/constant.rs | 34 +++++ src/lib/syntax/ast/expr.rs | 159 +++++++++++++++++++++++ src/lib/syntax/ast/mod.rs | 3 + src/lib/syntax/ast/op.rs | 226 +++++++++++++++++++++++++++++++++ src/lib/syntax/mod.rs | 4 + src/lib/syntax/parser.rs | 34 +++++ 7 files changed, 465 insertions(+), 5 deletions(-) create mode 100644 src/lib/syntax/ast/constant.rs create mode 100644 src/lib/syntax/ast/expr.rs create mode 100644 src/lib/syntax/ast/op.rs create mode 100644 src/lib/syntax/parser.rs diff --git a/Dockerfile b/Dockerfile index 6ca44e765c..7752305e00 100644 --- a/Dockerfile +++ b/Dockerfile @@ -6,16 +6,16 @@ COPY . . # LLDB Server EXPOSE 9228 -RUN apt-get update \ - apt-get upgrade \ - apt-get install -y software-properties-common +RUN apt-get -y update && \ + apt-get -y upgrade && \ + apt-get install -y sudo software-properties-common # https://askubuntu.com/questions/787383/how-to-install-llvm-3-9 # http://apt.llvm.org/ RUN wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | apt-key add - RUN apt-add-repository "deb http://apt.llvm.org/stretch/ llvm-toolchain-stretch-6.0 main" -RUN apt-get update -RUN apt-get install clang-6.0 lldb-6.0 +RUN apt-get -y update +# RUN sudo apt-get -y install clang-6.0 lldb-6.0 CMD ["/bin/bash"] \ No newline at end of file diff --git a/src/lib/syntax/ast/constant.rs b/src/lib/syntax/ast/constant.rs new file mode 100644 index 0000000000..f7c34fd1fb --- /dev/null +++ b/src/lib/syntax/ast/constant.rs @@ -0,0 +1,34 @@ +use std::fmt::{Display, Formatter, Result}; + +#[derive(Clone, PartialEq)] +/// A Javascript Constant +pub enum Const { + /// A UTF-8 string, such as `"Hello, world"` + String(String), + // A regular expression, such as `/where('s| is) [wW]ally/` + RegExp(String, bool, bool), + // A 64-bit floating-point number, such as `3.1415` + Num(f64), + // A 32-bit integer, such as `42` + Int(i32), + // A boolean, which is either `true` or `false` and is used to check if criteria are met + Bool(bool), + // The `null` value, which represents a non-existant value + Null, + // The `undefined` value, which represents a field or index that doesn't exist + Undefined, +} + +impl Display for Const { + fn fmt(&self, f: &mut Formatter) -> Result { + return match *self { + Const::String(ref st) => write!(f, "\"{}\"", st), + Const::RegExp(ref reg, _, _) => write!(f, "~/{}/", reg), + Const::Num(num) => write!(f, "{}", num), + Const::Int(num) => write!(f, "{}", num), + Const::Bool(v) => write!(f, "{}", v), + Const::Null => write!(f, "null"), + Const::Undefined => write!(f, "undefined"), + }; + } +} diff --git a/src/lib/syntax/ast/expr.rs b/src/lib/syntax/ast/expr.rs new file mode 100644 index 0000000000..3350f2c538 --- /dev/null +++ b/src/lib/syntax/ast/expr.rs @@ -0,0 +1,159 @@ +use std::collections::btree_map::BTreeMap; +use std::fmt::{Display, Formatter, Result}; +use syntax::ast::constant::Const; +use syntax::ast::op::{BinOp, Operator, UnaryOp}; +use syntax::ast::pos::Position; + +#[derive(Clone, PartialEq)] +pub struct Expr { + /// The expression definition + pub def: ExprDef, + /// The starting position + pub start: Position, + /// The ending position + pub end: Position, +} + +impl Expr { + /// Create a new expression with a starting and ending position + pub fn new(def: ExprDef, start: Position, end: Position) -> Expr { + Expr { + def: def, + start: start, + end: end, + } + } +} + +impl Display for Expr { + fn fmt(&self, f: &mut Formatter) -> Result { + write!(f, "{}", self.def) + } +} + +#[derive(Clone, PartialEq)] +/// A Javascript Expression +pub enum ExprDef { + /// Run a operation between 2 expressions + BinOpExpr(BinOp, Box, Box), + /// Run an operation on a value + UnaryOpExpr(UnaryOp, Box), + /// Make a constant value + ConstExpr(Const), + /// Construct an object from the function and arguments given + ConstructExpr(Box, Vec), + /// Run several expressions from top-to-bottom + BlockExpr(Vec), + /// Load a reference to a value + LocalExpr(String), + /// Gets the constant field of a value + GetConstFieldExpr(Box, String), + /// Gets the field of a value + GetFieldExpr(Box, Box), + /// Call a function with some values + CallExpr(Box, Vec), + /// Repeatedly run an expression while the conditional expression resolves to true + WhileLoopExpr(Box, Box), + /// Check if a conditional expression is true and run an expression if it is and another expression if it isn't + IfExpr(Box, Box, Option>), + /// Run blocks whose cases match the expression + SwitchExpr(Box, Vec<(Expr, Vec)>, Option>), + /// Create an object out of the binary tree given + ObjectDeclExpr(Box>), + /// Create an array with items inside + ArrayDeclExpr(Vec), + /// Create a function with the given name, arguments, and expression + FunctionDeclExpr(Option, Vec, Box), + /// Create an arrow function with the given arguments and expression + ArrowFunctionDeclExpr(Vec, Box), + /// Return the expression from a function + ReturnExpr(Option>), + /// Throw a value + ThrowExpr(Box), + /// Assign an expression to a value + AssignExpr(Box, Box), + /// A variable declaration + VarDeclExpr(Vec<(String, Option)>), + /// Return a string representing the type of the given expression + TypeOfExpr(Box), +} + +impl Operator for ExprDef { + fn get_assoc(&self) -> bool { + match *self { + ExprDef::ConstructExpr(_, _) + | ExprDef::UnaryOpExpr(_, _) + | ExprDef::TypeOfExpr(_) + | ExprDef::IfExpr(_, _, _) + | ExprDef::AssignExpr(_, _) => false, + _ => true, + } + } + fn get_precedence(&self) -> u64 { + match *self { + ExprDef::GetFieldExpr(_, _) | ExprDef::GetConstFieldExpr(_, _) => 1, + ExprDef::CallExpr(_, _) | ExprDef::ConstructExpr(_, _) => 2, + ExprDef::UnaryOpExpr(UnaryOp::IncrementPost, _) + | ExprDef::UnaryOpExpr(UnaryOp::IncrementPre, _) + | ExprDef::UnaryOpExpr(UnaryOp::DecrementPost, _) + | ExprDef::UnaryOpExpr(UnaryOp::DecrementPre, _) => 3, + ExprDef::UnaryOpExpr(UnaryOp::Not, _) + | ExprDef::UnaryOpExpr(UnaryOp::Minus, _) + | ExprDef::TypeOfExpr(_) => 4, + ExprDef::BinOpExpr(op, _, _) => op.get_precedence(), + ExprDef::IfExpr(_, _, _) => 15, + // 16 should be yield + ExprDef::AssignExpr(_, _) => 17, + _ => 19, + } + } +} + +impl Display for ExprDef { + fn fmt(&self, f: &mut Formatter) -> Result { + return match *self { + ExprDef::ConstExpr(ref c) => write!(f, "{}", c), + ExprDef::BlockExpr(ref block) => { + try!(write!(f, "{}", "{")); + for expr in block.iter() { + try!(write!(f, "{};", expr)); + } + write!(f, "{}", "}") + } + ExprDef::LocalExpr(ref s) => write!(f, "{}", s), + ExprDef::GetConstFieldExpr(ref ex, ref field) => write!(f, "{}.{}", ex, field), + ExprDef::GetFieldExpr(ref ex, ref field) => write!(f, "{}[{}]", ex, field), + ExprDef::CallExpr(ref ex, ref args) => { + try!(write!(f, "{}(", ex)); + let arg_strs: Vec = args.iter().map(|arg| arg.to_string()).collect(); + write!(f, "{})", arg_strs.connect(",")) + } + ExprDef::ConstructExpr(ref func, ref args) => write!(f, "new {}({})", func, args), + ExprDef::WhileLoopExpr(ref cond, ref expr) => write!(f, "while({}) {}", cond, expr), + ExprDef::IfExpr(ref cond, ref expr, None) => write!(f, "if({}) {}", cond, expr), + ExprDef::IfExpr(ref cond, ref expr, Some(ref else_e)) => { + write!(f, "if({}) {} else {}", cond, expr, else_e) + } + ExprDef::SwitchExpr(ref val, ref vals, None) => write!(f, "switch({}){}", val, vals), + ExprDef::SwitchExpr(ref val, ref vals, Some(ref def)) => { + write!(f, "switch({}){}default:{}", val, vals, def) + } + ExprDef::ObjectDeclExpr(ref map) => write!(f, "{}", map), + ExprDef::ArrayDeclExpr(ref arr) => write!(f, "{}", arr), + ExprDef::FunctionDeclExpr(ref name, ref args, ref expr) => { + write!(f, "function {}({}){}", name, args.connect(", "), expr) + } + ExprDef::ArrowFunctionDeclExpr(ref args, ref expr) => { + write!(f, "({}) => {}", args.connect(", "), expr) + } + ExprDef::BinOpExpr(ref op, ref a, ref b) => write!(f, "{} {} {}", a, op, b), + ExprDef::UnaryOpExpr(ref op, ref a) => write!(f, "{}{}", op, a), + ExprDef::ReturnExpr(Some(ref ex)) => write!(f, "return {}", ex), + ExprDef::ReturnExpr(None) => write!(f, "{}", "return"), + ExprDef::ThrowExpr(ref ex) => write!(f, "throw {}", ex), + ExprDef::AssignExpr(ref ref_e, ref val) => write!(f, "{} = {}", ref_e, val), + ExprDef::VarDeclExpr(ref vars) => write!(f, "var {}", vars), + ExprDef::TypeOfExpr(ref e) => write!(f, "typeof {}", e), + }; + } +} diff --git a/src/lib/syntax/ast/mod.rs b/src/lib/syntax/ast/mod.rs index a9cf49ed81..03cbbd536c 100644 --- a/src/lib/syntax/ast/mod.rs +++ b/src/lib/syntax/ast/mod.rs @@ -1,4 +1,7 @@ +pub mod constant; +pub mod expr; pub mod keyword; +pub mod op; pub mod pos; pub mod punc; pub mod token; diff --git a/src/lib/syntax/ast/op.rs b/src/lib/syntax/ast/op.rs new file mode 100644 index 0000000000..dd9f9334bd --- /dev/null +++ b/src/lib/syntax/ast/op.rs @@ -0,0 +1,226 @@ +use std::fmt::{Display, Formatter, Result}; + +/// Represents an operator +pub trait Operator { + /// Get the associativity as a boolean that is true if it goes rightwards + fn get_assoc(&self) -> bool; + /// Get the precedence as an unsigned integer, where the lower it is, the more precedence/priority it has + fn get_precedence(&self) -> u64; + /// Get the precedence and associativity of this operator + fn get_precedence_and_assoc(&self) -> (u64, bool) { + (self.get_precedence(), self.get_assoc()) + } +} + +#[derive(Clone, PartialEq)] +/// A numeric operation between 2 values +pub enum NumOp { + /// `a + b` - Addition + Add, + /// `a - b` - Subtraction + Sub, + /// `a / b` - Division + Div, + /// `a * b` - Multiplication + Mul, + /// `a % b` - Modulus + Mod, +} + +impl Display for NumOp { + fn fmt(&self, f: &mut Formatter) -> Result { + write!( + f, + "{}", + match *self { + NumOp::Add => "+", + NumOp::Sub => "-", + NumOp::Div => "/", + NumOp::Mul => "*", + NumOp::Mod => "%", + } + ) + } +} + +#[derive(Clone, PartialEq)] +/// A unary operation on a single value +pub enum UnaryOp { + /// `a++` - increment the value + IncrementPost, + /// `++a` - increment the value + IncrementPre, + /// `a--` - decrement the value + DecrementPost, + /// `--a` - decrement the value + DecrementPre, + /// `-a` - negate the value + Minus, + /// `+a` - convert to a number + Plus, + /// `!a` - get the opposite of the boolean value + Not, +} + +impl Display for UnaryOp { + fn fmt(&self, f: &mut Formatter) -> Result { + write!( + f, + "{}", + match *self { + UnaryOp::IncrementPost | UnaryOp::IncrementPre => "++", + UnaryOp::DecrementPost | UnaryOp::DecrementPre => "--", + UnaryOp::Plus => "+", + UnaryOp::Minus => "-", + UnaryOp::Not => "!", + } + ) + } +} + +#[derive(Clone, PartialEq)] +/// A bitwise operation between 2 values +pub enum BitOp { + /// `a & b` - Bitwise and + And, + /// `a | b` - Bitwise or + Or, + /// `a ^ b` - Bitwise xor + Xor, + /// `a << b` - Bit-shift leftwards + Shl, + /// `a >> b` - Bit-shift rightrights + Shr, +} + +impl Display for BitOp { + fn fmt(&self, f: &mut Formatter) -> Result { + write!( + f, + "{}", + match *self { + BitOp::And => "&", + BitOp::Or => "|", + BitOp::Xor => "^", + BitOp::Shl => "<<", + BitOp::Shr => ">>", + } + ) + } +} + +#[derive(Clone, PartialEq)] +/// A comparitive operation between 2 values +pub enum CompOp { + /// `a == b` - Equality + Equal, + /// `a != b` - Unequality + NotEqual, + /// `a === b` - Strict equality + StrictEqual, + /// `a !== b` - Strict unequality + StrictNotEqual, + /// `a > b` - If `a` is greater than `b` + GreaterThan, + /// `a >= b` - If `a` is greater than or equal to `b` + GreaterThanOrEqual, + /// `a < b` - If `a` is less than `b` + LessThan, + /// `a <= b` - If `a` is less than or equal to `b` + LessThanOrEqual, +} + +impl Display for CompOp { + fn fmt(&self, f: &mut Formatter) -> Result { + write!( + f, + "{}", + match *self { + CompOp::Equal => "==", + CompOp::NotEqual => "!=", + CompOp::StrictEqual => "===", + CompOp::StrictNotEqual => "!==", + CompOp::GreaterThan => ">", + CompOp::GreaterThanOrEqual => ">=", + CompOp::LessThan => "<", + CompOp::LessThanOrEqual => "<=", + } + ) + } +} + +#[derive(Clone, PartialEq)] +/// A logical operation between 2 boolean values +pub enum LogOp { + /// `a && b` - Logical and + And, + /// `a || b` - Logical or + Or, +} + +impl Display for LogOp { + fn fmt(&self, f: &mut Formatter) -> Result { + write!( + f, + "{}", + match *self { + LogOp::And => "&&", + LogOp::Or => "||", + } + ) + } +} + +#[derive(Clone, PartialEq)] +/// A binary operation between 2 values +pub enum BinOp { + /// Numeric operation + Num(NumOp), + /// Bitwise operation + Bit(BitOp), + /// Comparitive operation + Comp(CompOp), + /// Logical operation + Log(LogOp), +} + +impl Operator for BinOp { + fn get_assoc(&self) -> bool { + true + } + fn get_precedence(&self) -> u64 { + match *self { + BinOp::Num(NumOp::Mul) | BinOp::Num(NumOp::Div) | BinOp::Num(NumOp::Mod) => 5, + BinOp::Num(NumOp::Add) | BinOp::Num(NumOp::Sub) => 6, + BinOp::Bit(BitOp::Shl) | BinOp::Bit(BitOp::Shr) => 7, + BinOp::Comp(CompOp::LessThan) + | BinOp::Comp(CompOp::LessThanOrEqual) + | BinOp::Comp(CompOp::GreaterThan) + | BinOp::Comp(CompOp::GreaterThanOrEqual) => 8, + BinOp::Comp(CompOp::Equal) + | BinOp::Comp(CompOp::NotEqual) + | BinOp::Comp(CompOp::StrictEqual) + | BinOp::Comp(CompOp::StrictNotEqual) => 9, + BinOp::Bit(BitAnd) => 10, + BinOp::Bit(BitXor) => 11, + BinOp::Bit(BitOr) => 12, + BinOp::Log(LogAnd) => 13, + BinOp::Log(LogOr) => 14, + } + } +} + +impl Display for BinOp { + fn fmt(&self, f: &mut Formatter) -> Result { + write!( + f, + "{}", + match *self { + BinOp::Num(op) => op.to_string(), + BinOp::Bit(op) => op.to_string(), + BinOp::Comp(op) => op.to_string(), + BinOp::Log(op) => op.to_string(), + } + ) + } +} diff --git a/src/lib/syntax/mod.rs b/src/lib/syntax/mod.rs index c3f86b1376..44bc2efb36 100644 --- a/src/lib/syntax/mod.rs +++ b/src/lib/syntax/mod.rs @@ -1,2 +1,6 @@ +/// The Javascript Abstract Syntax Tree pub mod ast; +/// Parses a string stream into a sequence of tokens pub mod lexer; +/// Parses a sequence of tokens into expressions +pub mod parser; diff --git a/src/lib/syntax/parser.rs b/src/lib/syntax/parser.rs new file mode 100644 index 0000000000..78bfef596b --- /dev/null +++ b/src/lib/syntax/parser.rs @@ -0,0 +1,34 @@ +use syntax::ast::expr::Expr; +use syntax::ast::keyword::Keyword; +use syntax::ast::token::{Token, TokenData}; + +/// An error encounted during parsing an expression +pub enum ParseError { + /// When it expected a certain kind of token, but got another as part of something + Expected(Vec, Token, &'static str), + /// When it expected a certain expression, but got another + ExpectedExpr(&'static str, Expr), + /// When it didn't expect this keyword + UnexpectedKeyword(Keyword), + /// When there is an abrupt end to the parsing + AbruptEnd, +} + +pub type ParseResult = Result; + +pub struct Parser { + /// The tokens being input + tokens: Vec, + /// The current position within the tokens + pos: u64, +} + +impl Parser { + /// Create a new parser, using `tokens` as input + pub fn new(tokens: Vec) -> Parser { + Parser { + tokens: tokens, + pos: 0, + } + } +}