From fed2aef6e44cc1857404c51ac2cff10f2cb6b951 Mon Sep 17 00:00:00 2001 From: Garen Tyler Date: Sun, 29 Nov 2020 16:43:38 -0700 Subject: [PATCH] Working in Rust with Parser.bind(&mut String) --- .gitignore | 7 + Cargo.lock | 117 ++++++++++++++++ Cargo.toml | 11 ++ errors.txt | 0 output.s | 10 -- package.json | 20 --- src/ast.rs | 197 ++++++++++++++++++++++++++ src/ast.ts | 273 ------------------------------------ src/ast_old.rs | 235 +++++++++++++++++++++++++++++++ src/codegen.ts | 55 -------- src/index.ts | 10 -- src/lib.rs | 6 + src/lib.ts | 0 src/main.rs | 61 ++++++++ src/parse.rs | 371 +++++++++++++++++++++++++++++++++++++++++++++++++ src/parser.ts | 200 -------------------------- tsconfig.json | 7 - 17 files changed, 1005 insertions(+), 575 deletions(-) create mode 100644 Cargo.lock create mode 100644 Cargo.toml delete mode 100644 errors.txt delete mode 100644 output.s delete mode 100644 package.json create mode 100644 src/ast.rs delete mode 100644 src/ast.ts create mode 100644 src/ast_old.rs delete mode 100644 src/codegen.ts delete mode 100644 src/index.ts create mode 100644 src/lib.rs delete mode 100644 src/lib.ts create mode 100644 src/main.rs create mode 100644 src/parse.rs delete mode 100644 src/parser.ts delete mode 100644 tsconfig.json diff --git a/.gitignore b/.gitignore index ea8c4bf..32fc6a5 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,8 @@ /target + + +#Added by cargo +# +#already existing elements were commented out + +#/target diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..052e15f --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,117 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +[[package]] +name = "aho-corasick" +version = "0.7.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b476ce7103678b0c6d3d395dbbae31d48ff910bd28be979ba5d48c6351131d0d" +dependencies = [ + "memchr", +] + +[[package]] +name = "arrayvec" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b" + +[[package]] +name = "bitflags" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" + +[[package]] +name = "cfg-if" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "lexical-core" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db65c6da02e61f55dae90a0ae427b2a5f6b3e8db09f58d10efab23af92592616" +dependencies = [ + "arrayvec", + "bitflags", + "cfg-if", + "ryu", + "static_assertions", +] + +[[package]] +name = "memchr" +version = "2.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3728d817d99e5ac407411fa471ff9800a778d88a24685968b36824eaf4bee400" + +[[package]] +name = "nom" +version = "5.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffb4262d26ed83a1c0a33a38fe2bb15797329c85770da05e6b828ddb782627af" +dependencies = [ + "lexical-core", + "memchr", + "version_check", +] + +[[package]] +name = "pivot" +version = "0.1.0" +dependencies = [ + "nom", + "regex", +] + +[[package]] +name = "regex" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8963b85b8ce3074fecffde43b4b0dded83ce2f367dc8d363afc56679f3ee820b" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", + "thread_local", +] + +[[package]] +name = "regex-syntax" +version = "0.6.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8cab7a364d15cde1e505267766a2d3c4e22a843e1a601f0fa7564c0f82ced11c" + +[[package]] +name = "ryu" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e" + +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + +[[package]] +name = "thread_local" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d40c6d1b69745a6ec6fb1ca717914848da4b44ae29d9b3080cbee91d72a69b14" +dependencies = [ + "lazy_static", +] + +[[package]] +name = "version_check" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5a972e5669d67ba988ce3dc826706fb0a8b01471c088cb0b6110b805cc36aed" diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..5f855cb --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "pivot" +version = "0.1.0" +authors = ["Garen Tyler "] +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +regex = "1.4.1" +nom = "5.1.2" diff --git a/errors.txt b/errors.txt deleted file mode 100644 index e69de29..0000000 diff --git a/output.s b/output.s deleted file mode 100644 index fa57278..0000000 --- a/output.s +++ /dev/null @@ -1,10 +0,0 @@ -.global main -main: - push {fp, lr} - ldr r0, =1 - cmp r0, #1 - moveq r0, #'.' - movne r0, #'F' - bl putchar - mov r0, #0 - pop {fp, pc} diff --git a/package.json b/package.json deleted file mode 100644 index 1923719..0000000 --- a/package.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "name": "pivot", - "version": "1.0.0", - "description": "A programming language", - "main": "index.ts", - "scripts": { - "test": "echo \"Error: no test specified\" && exit 1", - "run": "deno run -c tsconfig.json --allow-read --allow-write --unstable src/index.ts" - }, - "repository": { - "type": "git", - "url": "git+https://github.com/ElementG9/pivot.git" - }, - "author": "Garen Tyler", - "license": "MIT", - "bugs": { - "url": "https://github.com/ElementG9/pivot/issues" - }, - "homepage": "https://github.com/ElementG9/pivot#readme" -} diff --git a/src/ast.rs b/src/ast.rs new file mode 100644 index 0000000..d0a0bfc --- /dev/null +++ b/src/ast.rs @@ -0,0 +1,197 @@ +#[derive(Clone, Debug, PartialEq)] +pub enum AstNodeKind { + // Primitives + Integer, + Identifier, + // Unary operators + Not, + // Infix operators + NotEqual, + Equal, + Add, + Subtract, + Multiply, + Divide, + // Control flow + Block, + IfStatement, + WhileLoop, + Program, + // Functions and variables + FunctionCall, + FunctionReturn, + FunctionDefinition, + VariableDefinition, + Assign, +} + +#[derive(Clone, Debug, PartialEq)] +pub struct AstNode { + pub kind: AstNodeKind, + pub value: String, + pub subnodes: Vec +} +impl AstNode { + pub fn new(kind: AstNodeKind, value: String, subnodes: Vec) -> AstNode { + AstNode { + kind, + value, + subnodes + } + } + pub fn emit(&self, f: &mut dyn std::fmt::Write) -> std::fmt::Result { + use AstNodeKind::*; + match self.kind { + Integer => write!(f, "i64.const {}\n", self.value), + Add => { + self.subnodes[0].emit(f)?; + self.subnodes[1].emit(f)?; + write!(f, "i64.add\n") + } + Program => { + write!(f, "(module\n")?; + for node in &self.subnodes { + node.emit(f); + } + write!(f, ")") + } + _ => Ok(()) + } + } + + // Primitives + pub fn integer(num: i64) -> AstNode { + AstNode { + kind: AstNodeKind::Integer, + value: num.to_string(), + subnodes: vec![], + } + } + pub fn identifier(id: String) -> AstNode { + AstNode { + kind: AstNodeKind::Identifier, + value: id, + subnodes: vec![], + } + } + // Unary operators + pub fn not(operand: AstNode) -> AstNode { + AstNode { + kind: AstNodeKind::Not, + value: "not".into(), + subnodes: vec![operand], + } + } + // Infix operators + pub fn not_equal(left: AstNode, right: AstNode) -> AstNode { + AstNode { + kind: AstNodeKind::NotEqual, + value: "not_equal".into(), + subnodes: vec![left, right] + } + } + pub fn equal(left: AstNode, right: AstNode) -> AstNode { + AstNode { + kind: AstNodeKind::Equal, + value: "equal".into(), + subnodes: vec![left, right] + } + } + pub fn add(left: AstNode, right: AstNode) -> AstNode { + AstNode { + kind: AstNodeKind::Add, + value: "add".into(), + subnodes: vec![left, right] + } + } + pub fn subtract(left: AstNode, right: AstNode) -> AstNode { + AstNode { + kind: AstNodeKind::Subtract, + value: "subtract".into(), + subnodes: vec![left, right] + } + } + pub fn multiply(left: AstNode, right: AstNode) -> AstNode { + AstNode { + kind: AstNodeKind::Multiply, + value: "multiply".into(), + subnodes: vec![left, right] + } + } + pub fn divide(left: AstNode, right: AstNode) -> AstNode { + AstNode { + kind: AstNodeKind::Divide, + value: "divide".into(), + subnodes: vec![left, right] + } + } + // Control flow + pub fn block(statements: Vec) -> AstNode { + AstNode { + kind: AstNodeKind::Block, + value: "block".into(), + subnodes: statements + } + } + pub fn if_statement(conditional: AstNode, consequence: AstNode, alternative: AstNode) -> AstNode { + AstNode { + kind: AstNodeKind::IfStatement, + value: "if_statement".into(), + subnodes: vec![conditional, consequence, alternative] + } + } + pub fn while_loop(conditional: AstNode, body: AstNode) -> AstNode { + AstNode { + kind: AstNodeKind::WhileLoop, + value: "while_loop".into(), + subnodes: vec![conditional, body], + } + } + pub fn program(statements: Vec) -> AstNode { + AstNode { + kind: AstNodeKind::Program, + value: "program".into(), + subnodes: statements + } + } + // Functions and variables + pub fn function_call(name: String, parameters: Vec) -> AstNode { + AstNode { + kind: AstNodeKind::FunctionCall, + value: name, + subnodes: parameters, + } + } + pub fn function_return(operand: AstNode) -> AstNode { + AstNode { + kind: AstNodeKind::FunctionReturn, + value: "return".into(), + subnodes: vec![operand], + } + } + pub fn function_definition(name: String, parameters: Vec, body: AstNode) -> AstNode { + let mut params = vec![body]; + for p in parameters { + params.push(p); + } + AstNode { + kind: AstNodeKind::FunctionDefinition, + value: name, + subnodes: params, + } + } + pub fn variable_definition(name: String, value: AstNode) -> AstNode { + AstNode { + kind: AstNodeKind::VariableDefinition, + value: name, + subnodes: vec![value], + } + } + pub fn assign(name: String, value: AstNode) -> AstNode { + AstNode { + kind: AstNodeKind::Assign, + value: name, + subnodes: vec![value], + } + } +} diff --git a/src/ast.ts b/src/ast.ts deleted file mode 100644 index fb5c9ff..0000000 --- a/src/ast.ts +++ /dev/null @@ -1,273 +0,0 @@ -import { emit } from './codegen.ts'; - -export interface AST { - emit(): void; - equals(other: AST): boolean; -} - -// Number node. -export class Num implements AST { - constructor(public value: number) {} - - emit() { - emit(` ldr r0, =${this.value}`); - } - equals(other: AST): boolean { - return other instanceof Num - && this.value === other.value; - } -} -// Identifier node. -export class Id implements AST { - constructor(public value: string) {} - - emit() { - throw Error('Not implemented yet'); - } - equals(other: AST): boolean { - return other instanceof Id - && this.value === other.value; - } -} -// Operator nodes. -export class Not implements AST { - constructor(public operand: AST) {} - - emit() { - this.operand.emit(); - emit(' cmp r0, #0'); - emit(' moveq r0, #1'); - emit(' movne r0, #0'); - } - equals(other: AST): boolean { - return other instanceof Not - && this.operand.equals(other.operand); - } -} -export abstract class Infix { - constructor(public left: AST, public right: AST) {} - emit() { - this.left.emit(); - emit(' push {r0, ip}'); - this.right.emit(); - emit(' pop {r1, ip}'); - } -} -export class Equal extends Infix implements AST { - constructor(public left: AST, public right: AST) { - super(left, right); - } - - emit() { - super.emit(); - emit(' cmp r0, r1'); - emit(' moveq r0, #1'); - emit(' movne r0, #0'); - } - equals(other: AST): boolean { - return other instanceof Equal - && this.left.equals(other.left) - && this.right.equals(other.right); - } -} -export class NotEqual extends Infix implements AST { - constructor(public left: AST, public right: AST) { - super(left, right); - } - - emit() { - super.emit(); - emit(' cmp r0, r1'); - emit(' moveq r0, #0'); - emit(' movne r0, #1'); - } - equals(other: AST): boolean { - return other instanceof NotEqual - && this.left.equals(other.left) - && this.right.equals(other.right); - } -} -export class Add extends Infix implements AST { - constructor(public left: AST, public right: AST) { - super(left, right); - } - - emit() { - super.emit(); - emit(' add r0, r0, r1'); - } - equals(other: AST): boolean { - return other instanceof Add - && this.left.equals(other.left) - && this.right.equals(other.right); - } -} -export class Subtract extends Infix implements AST { - constructor(public left: AST, public right: AST) { - super(left, right); - } - - emit() { - super.emit(); - emit(' sub r0, r0, r1'); - } - equals(other: AST): boolean { - return other instanceof Subtract - && this.left.equals(other.left) - && this.right.equals(other.right); - } -} -export class Multiply extends Infix implements AST { - constructor(public left: AST, public right: AST) { - super(left, right); - } - - emit() { - super.emit(); - emit(' mul r0, r0, r1'); - } - equals(other: AST): boolean { - return other instanceof Multiply - && this.left.equals(other.left) - && this.right.equals(other.right); - } -} -export class Divide extends Infix implements AST { - constructor(public left: AST, public right: AST) { - super(left, right); - } - - emit() { - super.emit(); - emit(' udiv r0, r0, r1'); - } - equals(other: AST): boolean { - return other instanceof Divide - && this.left.equals(other.left) - && this.right.equals(other.right); - } -} -// Function call node. -export class FunctionCall implements AST { - constructor(public callee: string, public args: Array) {} - - emit() { - throw Error('Not implemented yet'); - } - equals(other: AST): boolean { - return other instanceof FunctionCall - && this.callee === other.callee - && this.args.length === other.args.length - && this.args.every((arg: AST, i: number) => arg.equals(other.args[i])); - } -} -// Return node. -export class Return implements AST { - constructor(public operand: AST) {} - - emit() { - throw Error('Not implemented yet'); - } - equals(other: AST): boolean { - return other instanceof Return - && this.operand.equals(other.operand); - } -} -// Block node. -export class Block implements AST { - constructor(public statements: Array) {} - - emit() { - this.statements.forEach((statement: any) => statement.emit()); - } - equals(other: AST): boolean { - return other instanceof Block - && this.statements.length === other.statements.length - && this.statements.every((arg: AST, i: number) => arg.equals(other.statements[i])); - } -} -// If node. -export class If implements AST { - constructor(public conditional: AST, - public consequence: AST, - public alternative: AST) {} - - emit() { - throw Error('Not implemented yet'); - } - equals(other: AST): boolean { - return other instanceof If - && this.conditional.equals(other.conditional) - && this.consequence.equals(other.consequence) - && this.alternative.equals(other.alternative); - } -} -// Function definition node. -export class FunctionDefinition implements AST { - constructor(public name: string, - public parameters: Array, - public body: AST) {} - - emit() { - throw Error(`Not implemented yet (${this.name})`); - } - equals(other: AST): boolean { - return other instanceof FunctionDefinition - && this.name === other.name - && this.parameters.length === other.parameters.length - && this.parameters.every((arg: string, i: number) => arg === other.parameters[i]) - && this.body.equals(other.body); - } -} -// Variable declaration node. -export class VariableDeclaration implements AST { - constructor(public name: string, public value: AST) {} - - emit() { - throw Error('Not implemented yet'); - } - equals(other: AST): boolean { - return other instanceof VariableDeclaration - && this.name === other.name - && this.value.equals(other.value); - } -} -// Assignment node. -export class Assign implements AST { - constructor(public name: string, public value: AST) {} - - emit() { - throw Error('Not implemented yet'); - } - equals(other: AST): boolean { - return other instanceof Assign - && this.name === other.name - && this.value.equals(other.value); - } -} -// While loop node. -export class While implements AST { - constructor(public conditional: AST, public body: AST) {} - - emit() { - throw Error('Not implemented yet'); - } - equals(other: AST): boolean { - return other instanceof While - && this.conditional.equals(other.conditional) - && this.body.equals(other.body); - } -} -// Variable node. -export class Var implements AST { - constructor(public name: string, public value: AST) {} - - emit() { - throw Error('Not implemented yet'); - } - equals(other: AST): boolean { - return other instanceof Var - && this.name === other.name - && this.value.equals(other.value); - } -} diff --git a/src/ast_old.rs b/src/ast_old.rs new file mode 100644 index 0000000..c1e9472 --- /dev/null +++ b/src/ast_old.rs @@ -0,0 +1,235 @@ +use std::fmt::Write; + +#[derive(Debug, PartialEq)] +pub struct AstNode { + pub value: Option, + pub kind: String, + pub subtokens: Option>, +} +impl std::fmt::Display for AstNode { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match &self.kind[..] { + "number" => write!(f, "{}", self.value.as_ref().expect("number had blank value")), + "identifier" => write!(f, "{}", self.value.as_ref().expect("number had blank value")), + "not" => write!(f, "!({})", self.subtokens.as_ref().expect("not had blank subtokens")[0]), + "equal" => write!(f, "({} == ({}))", self.subtokens.as_ref().expect("equal had blank subtokens")[0], self.subtokens.as_ref().expect("equal had blank subtokens")[1]), + "not_equal" => write!(f, "({} != {})", self.subtokens.as_ref().expect("not_equal had blank subtokens")[0], self.subtokens.as_ref().expect("not_equal had blank subtokens")[1]), + "add" => write!(f, "({} + {})", self.subtokens.as_ref().expect("add had blank subtokens")[0], self.subtokens.as_ref().expect("add had blank subtokens")[1]), + "subtract" => write!(f, "({} - {})", self.subtokens.as_ref().expect("subtract had blank subtokens")[0], self.subtokens.as_ref().expect("subtract had blank subtokens")[1]), + "multiply" => write!(f, "({} * {})", self.subtokens.as_ref().expect("multiply had blank subtokens")[0], self.subtokens.as_ref().expect("multiply had blank subtokens")[1]), + "divide" => write!(f, "({} / {})", self.subtokens.as_ref().expect("divide had blank subtokens")[0], self.subtokens.as_ref().expect("divide had blank subtokens")[1]), + "call" => { + write!(f, "({}(", self.value.as_ref().expect("call had blank value"))?; + let args = self.subtokens.as_ref().expect("call had blank subtokens"); + if args.len() > 0 { + write!(f, "{}", args[0])?; + if args.len() > 1 { + for i in 1..args.len() { + write!(f, ", {}", args[i])?; + } + } + } + write!(f, "))") + }, + "return" => write!(f, "return {}", self.subtokens.as_ref().expect("return had blank subtokens")[0]), + "block" => { + write!(f, "{{\n")?; + let stmts = self.subtokens.as_ref().expect("block had blank subtokens"); + if stmts.len() > 0 { + write!(f, "{};", stmts[0])?; + if stmts.len() > 1 { + for i in 1..stmts.len() { + write!(f, "\n{};", stmts[i])?; + } + } + } + write!(f, "\n}}") + } + "if" => { + let parts = self.subtokens.as_ref().expect("if had blank subtokens"); + write!(f, "(if ({}) {{{}}} else {{{}}})", parts[0], parts[1], parts[2]) + } + "function" => { + let parts = self.subtokens.as_ref().expect("function had blank subtokens"); + write!(f, "function {}(", self.value.as_ref().expect("function had blank value"))?; + let params = &parts[1..]; + if params.len() > 0 { + write!(f, "{}", params[0])?; + if params.len() > 1 { + for i in 1..params.len() { + write!(f, ", {}", params[i])?; + } + } + } + write!(f, ") {}", parts[0]) + } + "variable" => write!(f, "var {} = {}", self.value.as_ref().expect("var had blank value"), self.subtokens.as_ref().expect("var had blank subtokens")[0]), + "assign" => write!(f, "{} = {}", self.value.as_ref().expect("assign had blank value"), self.subtokens.as_ref().expect("assign had blank subtokens")[0]), + "while" => { + let parts = self.subtokens.as_ref().expect("while had blank subtokens"); + write!(f, "while ({})\n{}\n", parts[0], parts[1]) + } + "program" => { + write!(f, "{{\n")?; + let stmts = self.subtokens.as_ref().expect("program had blank subtokens"); + if stmts.len() > 0 { + write!(f, "{};", stmts[0])?; + if stmts.len() > 1 { + for i in 1..stmts.len() { + write!(f, "\n{};", stmts[i])?; + } + } + } + write!(f, "\n}}") + } + _ => write!(f, "(unknown node type {})", self.kind), + } + } +} +impl AstNode { + pub fn emit(&self, f: &mut dyn Write) -> Result<(), std::fmt::Error> { + match &self.kind[..] { + "number" => write!(f, "i32.const {}\n", self.value.as_ref().expect("number had blank value")), + "add" => { + let subtokens = self.subtokens.as_ref().expect("add had blank subtokens"); + subtokens[0].emit(f)?; + subtokens[1].emit(f)?; + write!(f, "i32.add\n") + }, + _ => Ok(()) + } + } + + pub fn number(num: i32) -> AstNode { + AstNode { + value: Some(num.to_string()), + kind: "number".into(), + subtokens: None, + } + } + pub fn identifier>(id: T) -> AstNode { + AstNode { + value: Some(id.into()), + kind: "identifier".into(), + subtokens: None, + } + } + pub fn not(operand: AstNode) -> AstNode { + AstNode { + value: None, + kind: "not".into(), + subtokens: Some(vec![operand]), + } + } + pub fn equal(left: AstNode, right: AstNode) -> AstNode { + AstNode { + value: None, + kind: "equal".into(), + subtokens: Some(vec![left, right]), + } + } + pub fn not_equal(left: AstNode, right: AstNode) -> AstNode { + AstNode { + value: None, + kind: "not_equal".into(), + subtokens: Some(vec![left, right]), + } + } + pub fn add(left: AstNode, right: AstNode) -> AstNode { + AstNode { + value: None, + kind: "add".into(), + subtokens: Some(vec![left, right]), + } + } + pub fn subtract(left: AstNode, right: AstNode) -> AstNode { + AstNode { + value: None, + kind: "subtract".into(), + subtokens: Some(vec![left, right]), + } + } + pub fn multiply(left: AstNode, right: AstNode) -> AstNode { + AstNode { + value: None, + kind: "multiply".into(), + subtokens: Some(vec![left, right]), + } + } + pub fn divide(left: AstNode, right: AstNode) -> AstNode { + AstNode { + value: None, + kind: "divide".into(), + subtokens: Some(vec![left, right]), + } + } + pub fn call>(callee: T, args: Vec) -> AstNode { + AstNode { + value: Some(callee.into()), + kind: "call".into(), + subtokens: Some(args), + } + } + pub fn r#return(operand: AstNode) -> AstNode { + AstNode { + value: None, + kind: "return".into(), + subtokens: Some(vec![operand]), + } + } + pub fn block(statements: Vec) -> AstNode { + AstNode { + value: None, + kind: "block".into(), + subtokens: Some(statements), + } + } + pub fn r#if(conditional: AstNode, consequence: AstNode, alternative: AstNode) -> AstNode { + AstNode { + value: None, + kind: "if".into(), + subtokens: Some(vec![conditional, consequence, alternative]), + } + } + pub fn function>(name: T, parameters: Vec<(T, T)>, body: AstNode) -> AstNode { + // Turn the parameter strings into ids. + let mut params = vec![]; + params.push(body); // First one will always be the body. + for p in parameters { + params.push(AstNode::identifier(p)); + } + AstNode { + value: Some(name.into()), + kind: "function".into(), + subtokens: Some(params), + } + } + pub fn variable>(name: T, value: AstNode) -> AstNode { + AstNode { + value: Some(name.into()), + kind: "variable".into(), + subtokens: Some(vec![value]), + } + } + pub fn assign>(name: T, value: AstNode) -> AstNode { + AstNode { + value: Some(name.into()), + kind: "assign".into(), + subtokens: Some(vec![value]), + } + } + pub fn r#while(conditional: AstNode, body: AstNode) -> AstNode { + AstNode { + value: None, + kind: "while".into(), + subtokens: Some(vec![conditional, body]), + } + } + pub fn program(statements: Vec) -> AstNode { + AstNode { + value: None, + kind: "program".into(), + subtokens: Some(statements), + } + } +} diff --git a/src/codegen.ts b/src/codegen.ts deleted file mode 100644 index 84202de..0000000 --- a/src/codegen.ts +++ /dev/null @@ -1,55 +0,0 @@ -import { AST } from './ast.ts'; -import { existsSync } from 'https://deno.land/std/fs/mod.ts'; - -const outputFileName = '../output.s'; - -const outputToFile = false; -export async function emit(input: string) { - if (outputToFile) { - if (existsSync(outputFileName)) { - await Deno.remove(outputFileName); - } - await Deno.create(outputFileName); - const outputFile = await Deno.open(outputFileName, { - write: true, - append: true, - }); - await Deno.write(outputFile.rid, new TextEncoder().encode(input)); - } else { - console.log(input); - } -} - -export class Main implements AST { - constructor(public statements: Array) {} - - emit() { - emit('.global main'); - emit('main:'); - emit(' push {fp, lr}'); - this.statements.forEach((statement: any) => statement.emit()); - emit(' mov r0, #0'); - emit(' pop {fp, pc}'); - } - equals(other: AST): boolean { - return other instanceof Main - && this.statements.length === other.statements.length - && this.statements.every((arg: AST, i: number) => arg.equals(other.statements[i])); - } -} - -export class Assert implements AST { - constructor(public condition: AST) {} - - emit() { - this.condition.emit(); - emit(' cmp r0, #1'); - emit(` moveq r0, #'.'`); - emit(` movne r0, #'F'`); - emit(' bl putchar'); - } - equals(other: AST): boolean { - return other instanceof Assert - && this.condition.equals(other.condition); - } -} diff --git a/src/index.ts b/src/index.ts deleted file mode 100644 index 141f84b..0000000 --- a/src/index.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { parse } from './parser.ts'; - -let result = parse(` -function main() { - assert(1); - assert(!0); -} -`); -// console.log(JSON.stringify(result, null, 2)); -result.emit(); diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..8858c9c --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,6 @@ +#[macro_use] +extern crate nom; +extern crate regex; + +pub mod ast; +pub mod parse; diff --git a/src/lib.ts b/src/lib.ts deleted file mode 100644 index e69de29..0000000 diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..aba00b9 --- /dev/null +++ b/src/main.rs @@ -0,0 +1,61 @@ +use pivot::ast::AstNode; +use regex::Regex; + +fn main() { + pivot::parse::parse(); + // let src = AstNode::program(vec![ + // AstNode::add( + // AstNode::integer(2), + // AstNode::integer(3), + // ) + // ]); + // println!("{:?}", src); + // let mut out = String::new(); + // src.emit(&mut out); + // println!("{}", out); + // test(); +} + +// fn test() { +// use pivot::ast::AstNode; +// +// let src = r#" +// function factorial(n) { +// var result = 1; +// while (n != 1) { +// result = result * n; +// n = n - 1; +// } +// return result; +// } +// "#; +// +// let actual_ast = pivot::parse::parse(); +// let expected_ast = AstNode::function("factorial", vec!["n"], +// AstNode::block(vec![ +// AstNode::variable("result", AstNode::number(1)), +// AstNode::r#while( +// AstNode::not_equal( +// AstNode::identifier("n"), +// AstNode::number(1) +// ), +// AstNode::block(vec![ +// AstNode::assign("result", +// AstNode::multiply( +// AstNode::identifier("result"), +// AstNode::identifier("n") +// ) +// ), +// AstNode::assign("n", +// AstNode::subtract( +// AstNode::identifier("n"), +// AstNode::number(1) +// ) +// ) +// ]), +// ), +// AstNode::r#return(AstNode::identifier("result")) +// ]) +// ); +// println!("{}", expected_ast); +// } diff --git a/src/parse.rs b/src/parse.rs new file mode 100644 index 0000000..6215219 --- /dev/null +++ b/src/parse.rs @@ -0,0 +1,371 @@ +use crate::ast::AstNode; +use std::ops::Range; +use regex::Regex; + +pub fn parse() -> AstNode { + let src = "420"; + let mut n = String::new(); + + println!("src: {:?}", src); + println!("n: {:?}", n); + { + let mut num = Parser::regex(r"\d+(\.\d+)?").bind(&mut n); + + let mut full = num; + println!("full: {}", full); + println!("full.parse: {:?}", full.parse(src)); + println!("full: {}", full); + } + println!("src: {:?}", src); + println!("n: {:?}", n); + AstNode::block(vec![]) +} + +#[derive(Debug)] +pub enum ParserKind<'a> { + Literal(String), + Regex(Regex), + And, + Or, + Repeat(usize), + RepeatRange(Range), + Bind(&'a mut String), +} +impl<'a> std::fmt::Display for ParserKind<'a> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + use ParserKind::*; + match self { + Literal(s) => write!(f, "Literal \"{}\"", s), + Regex(r) => write!(f, "Regex /{}/", r.as_str()), + And => write!(f, "And"), + Or => write!(f, "Or"), + Repeat(num) => write!(f, "Repeat {}", num), + RepeatRange(range) => write!(f, "RepeatRange {:?}", range), + Bind(_) => write!(f, "Bind"), + } + } +} + +#[derive(Debug)] +pub struct Parser<'a> { + kind: ParserKind<'a>, + subparsers: Vec>, + // bind: Option<&'a mut String>, +} +impl<'a> std::fmt::Display for Parser<'a> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + self.pretty_print(f, 0) + } +} +impl<'a> Parser<'a> { + pub fn parse>(&mut self, src: T) -> Result<(String, String), String> { + use ParserKind::*; + let s: String = src.into(); + match &mut self.kind { + Literal(literal) => { + if s.len() >= literal.len() && s[..literal.len()] == literal[..] { + Ok((s[..literal.len()].to_owned(), s[literal.len()..].to_owned())) + } else { + Err(s) + } + } + Regex(re) => { + if let Some(mat) = re.find(&s) { + if mat.start() == 0 { + Ok((s[mat.start()..mat.end()].to_owned(), s[mat.end()..].to_owned())) + } else { + Err(s) + } + } else { + Err(s) + } + } + And => { + let (lmatched, lrest) = self.subparsers[0].parse(s)?; + let (rmatched, rrest) = self.subparsers[1].parse(lrest)?; + Ok((lmatched + &rmatched, rrest)) + } + Or => { + if let Ok(lresult) = self.subparsers[0].parse(s.clone()) { + Ok(lresult) + } else { + self.subparsers[1].parse(s.clone()) + } + } + Repeat(num_repeats) => { + let mut matched = String::new(); + let mut rest = s.clone(); + for _ in 0..*num_repeats { + let (m, r) = self.subparsers[0].parse(rest)?; + matched += &m; + rest = r; + } + Ok((matched, rest)) + } + RepeatRange(range) => { + let mut matched = String::new(); + let mut rest = s.clone(); + + // Parse up to range.start + for _ in 0..range.start { + let (m, r) = self.subparsers[0].parse(rest)?; + matched += &m; + rest = r; + } + + // Parse optionally up to range.end + for _ in 0..(range.end - range.start) { + let parse_result = self.subparsers[0].parse(rest); + if let Err(r) = parse_result { + rest = r; + break; + } else { + let (m, r) = parse_result.unwrap(); + matched += &m; + rest = r; + } + } + + Ok((matched, rest)) + } + Bind(var) => { + let (matched, rest) = self.subparsers[0].parse(s)?; + **var = matched.clone(); + Ok((matched, rest)) + } + } + } + + // Static + pub fn literal>(s: T) -> Parser<'a> { + Parser { + kind: ParserKind::Literal(s.into()), + subparsers: vec![], + // bind: None, + } + } + pub fn regex>(s: T) -> Parser<'a> { + Parser { + kind: ParserKind::Regex(Regex::new(&s.into()).expect("could not compile regex")), + subparsers: vec![], + // bind: None, + } + } + + // Instance + pub fn and(self, r: Parser<'a>) -> Parser<'a> { + Parser { + kind: ParserKind::And, + subparsers: vec![self, r], + // bind: None, + } + } + pub fn or(self, r: Parser<'a>) -> Parser<'a> { + Parser { + kind: ParserKind::Or, + subparsers: vec![self, r], + // bind: None, + } + } + pub fn repeat(self, num_repeats: usize) -> Parser<'a> { + Parser { + kind: ParserKind::Repeat(num_repeats), + subparsers: vec![self], + // bind: None, + } + } + pub fn repeat_range(self, num_repeats: Range) -> Parser<'a> { + Parser { + kind: ParserKind::RepeatRange(num_repeats), + subparsers: vec![self], + // bind: None, + } + } + pub fn optional(self) -> Parser<'a> { + Parser { + kind: ParserKind::RepeatRange(0..1), + subparsers: vec![self], + // bind: None, + } + } + pub fn bind(self, s: &'a mut String) -> Parser<'a> { + Parser { + kind: ParserKind::Bind(s), + subparsers: vec![self], + } + } + + // Other + pub fn pretty_print(&self, f: &mut std::fmt::Formatter<'_>, indent: usize) -> std::fmt::Result { + for _ in 0..indent { + write!(f, " "); + } + write!(f, "{}", self.kind)?; + if self.subparsers.len() > 0 { + write!(f, " [\n")?; + for subparser in &self.subparsers { + subparser.pretty_print(f, indent + 2)?; + write!(f, ",\n")?; + } + for _ in 0..indent { + write!(f, " ")?; + } + write!(f, "]") + } else { + write!(f, "") + } + } +} + +// use combinators::*; +// pub mod combinators { +// pub struct Parser<'a> { +// source: &'a str, +// subparsers: Vec>, +// pub parse: Box Result<(&'a str, &'a str), &'a str>>, +// } +// impl Parser { +// // pub type S = Into; +// pub fn literal<'a>(literal: &'a str) -> Parser { +// Parser { +// source: literal.into(), +// subparsers: vec![], +// parse: Box::new(|s: &'a str| -> Result<(&'a str, &'a str), &'a str> { +// if src.len() >= literal.len() { +// if src[..literal.len()] == literal[..] { +// return Ok((&src[..literal.len()], &src[literal.len()..])); +// } +// } +// Err(&src[..]) +// }) +// } +// } +// } + // pub fn literal<'a, T>(literal: &'a str) -> T where T: Fn(&str) -> Result<(&str, &str), &str> + 'a { + // move |src: &str| -> Result<(&str, &str), &str> { + // if src.len() >= literal.len() { + // if src[..literal.len()] == literal[..] { + // return Ok((&src[..literal.len()], &src[literal.len()..])); + // } + // } + // Err(&src[..]) + // } + // } + // pub fn and<'a>(left: T, right: T) -> T where T: Fn(&str) -> Result<(&str, &str), &str> + 'a { + // + // } +// } + +// // Whitespace and comments. +// let whitespace = Parser.regex(/[ \n\r\t]+/y); +// let comments = Parser.regex(/[/][/].*/y).or( +// Parser.regex(/[/][*].*[*][/]/sy) +// ); +// let ignored = Parser.zeroOrMore(whitespace.or(comments)); +// // Tokens +// let token = (pattern: RegExp) => Parser.regex(pattern).bind((value: any) => ignored.and(Parser.constant(value))); +// let FUNCTION = token(/function\b/y); +// let IF = token(/if\b/y); +// let ELSE = token(/else\b/y); +// let RETURN = token(/return\b/y); +// let ASSIGN = token(/=/y).map(_ => Assign); +// let VAR = token(/var\b/y); +// let WHILE = token(/while\b/y); +// let COMMA = token(/[,]/y); +// let SEMICOLON = token(/[;]/y); +// let LEFT_PAREN = token(/[(]/y); +// let RIGHT_PAREN = token(/[)]/y); +// let LEFT_BRACE = token(/[{]/y); +// let RIGHT_BRACE = token(/[}]/y); +// let NUMBER = token(/[0-9]/y).map((digits: any) => new Num(parseInt(digits))); +// let ID = token(/[a-zA-Z_][a-zA-Z0-9_]*/y).map((x: any) => new Id(x)); +// let NOT = token(/!/y).map(_ => Not); +// let EQUAL = token(/==/y).map(_ => Equal); +// let NOT_EQUAL = token(/!=/y).map(_ => NotEqual); +// let PLUS = token(/[+]/y).map(_ => Add); +// let MINUS = token(/[-]/y).map(_ => Subtract); +// let STAR = token(/[*]/y).map(_ => Multiply); +// let SLASH = token(/[/]/y).map(_ => Divide); +// // Expression parser +// let expression: Parser = Parser.error('expression parser used before definition'); +// // Call parser +// let args: Parser> = expression.bind((arg: any) => Parser.zeroOrMore(COMMA.and(expression)).bind((args: any) => Parser.constant([arg, ...args]))).or(Parser.constant([])); +// let functionCall: Parser = ID.bind((callee: any) => LEFT_PAREN.and(args.bind((args: any) => RIGHT_PAREN.and(Parser.constant(callee.equals(new Id('assert')) ? new Assert(args[0]) : new FunctionCall(callee, args)))))); +// // Atom +// let atom: Parser = functionCall.or(ID).or(NUMBER).or(LEFT_PAREN.and(expression).bind((e: any) => RIGHT_PAREN.and(Parser.constant(e)))); +// // Unary operators +// let unary: Parser = Parser.optional(NOT).bind((not: any) => atom.map((operand: any) => not ? new Not(operand) : operand)); +// // Infix operators +// let infix = (operatorParser: any, operandParser: any) => +// operandParser.bind((operand: any) => +// Parser.zeroOrMore( +// operatorParser.bind((operator: any) => +// operandParser.bind((operand: any) => +// Parser.constant({ operator, operand }) +// ) +// ) +// ).map((operatorTerms: any) => +// operatorTerms.reduce((left: any, { operator, operand }: { operator: any, operand: any }) => +// new operator(left, operand), operand) +// ) +// ); +// let product = infix(STAR.or(SLASH), unary); +// let sum = infix(PLUS.or(MINUS), product); +// let comparison = infix(EQUAL.or(NOT_EQUAL), sum); +// // Associativity +// // Closing the loop: expression +// expression.parse = comparison.parse; +// // Statement +// let statement: Parser = Parser.error('statement parser used before definition'); +// let returnStatement: Parser = RETURN.and(expression).bind((operand: any) => SEMICOLON.and(Parser.constant(new Return(operand)))); +// let expressionStatement: Parser = expression.bind((operand: any) => SEMICOLON.and(Parser.constant(operand))); +// let ifStatement: Parser = IF.and(LEFT_PAREN).and(expression).bind((conditional: any) => +// RIGHT_PAREN.and(statement).bind((consequence: any) => +// ELSE.and(statement).bind((alternative: any) => +// Parser.constant(new If(conditional, consequence, alternative)) +// ) +// ) +// ); +// let whileStatement: Parser = WHILE.and(LEFT_PAREN).and(expression).bind((conditional: any) => +// RIGHT_PAREN.and(statement).bind((body: any) => +// Parser.constant(new While(conditional, body)) +// ) +// ); +// let varStatement: Parser = VAR.and(ID).bind((name: any) => +// ASSIGN.and(expression).bind((value: any) => +// SEMICOLON.and(Parser.constant(new Var(name, value))) +// ) +// ); +// let assignmentStatement: Parser = ID.bind((name: any) => +// ASSIGN.and(expression).bind((value: any) => +// SEMICOLON.and(Parser.constant(new Assign(name, value))) +// ) +// ); +// let blockStatement: Parser = LEFT_BRACE.and(Parser.zeroOrMore(statement)).bind((statements: any) => +// RIGHT_BRACE.and(Parser.constant(new Block(statements))) +// ); +// let parameters: Parser> = ID.bind((param: any) => +// Parser.zeroOrMore(COMMA.and(ID)).bind((params: any) => +// Parser.constant([param, ...params]) +// ) +// ).or(Parser.constant([])); +// let functionStatement: Parser = FUNCTION.and(ID).bind((name: any) => +// LEFT_PAREN.and(parameters).bind((parameters: any) => +// RIGHT_PAREN.and(blockStatement).bind((block: any) => +// Parser.constant(name.equals(new Id('main')) ? new Main(block.statements) : new FunctionDefinition(name, parameters, block)) +// ) +// ) +// ); +// let statementParser: Parser = +// returnStatement +// .or(functionStatement) +// .or(ifStatement) +// .or(whileStatement) +// .or(varStatement) +// .or(assignmentStatement) +// .or(blockStatement) +// .or(expressionStatement); +// statement.parse = statementParser.parse; +// let parser: Parser = ignored.and(Parser.zeroOrMore(statement)).map((statements: any) => new Block(statements)); +// +// return parser.parseStringToCompletion(source); diff --git a/src/parser.ts b/src/parser.ts deleted file mode 100644 index 3670eae..0000000 --- a/src/parser.ts +++ /dev/null @@ -1,200 +0,0 @@ -import { AST, Num, Id, Not, Equal, NotEqual, Add, Subtract, Multiply, Divide, FunctionCall, Return, If, While, Var, Assign, Block, FunctionDefinition } from './ast.ts'; -import { Main, Assert } from './codegen.ts'; - -export function parse(source: string): AST { - // Whitespace and comments. - let whitespace = Parser.regex(/[ \n\r\t]+/y); - let comments = Parser.regex(/[/][/].*/y).or( - Parser.regex(/[/][*].*[*][/]/sy) - ); - let ignored = Parser.zeroOrMore(whitespace.or(comments)); - // Tokens - let token = (pattern: RegExp) => Parser.regex(pattern).bind((value: any) => ignored.and(Parser.constant(value))); - let FUNCTION = token(/function\b/y); - let IF = token(/if\b/y); - let ELSE = token(/else\b/y); - let RETURN = token(/return\b/y); - let ASSIGN = token(/=/y).map(_ => Assign); - let VAR = token(/var\b/y); - let WHILE = token(/while\b/y); - let COMMA = token(/[,]/y); - let SEMICOLON = token(/[;]/y); - let LEFT_PAREN = token(/[(]/y); - let RIGHT_PAREN = token(/[)]/y); - let LEFT_BRACE = token(/[{]/y); - let RIGHT_BRACE = token(/[}]/y); - let NUMBER = token(/[0-9]/y).map((digits: any) => new Num(parseInt(digits))); - let ID = token(/[a-zA-Z_][a-zA-Z0-9_]*/y).map((x: any) => new Id(x)); - let NOT = token(/!/y).map(_ => Not); - let EQUAL = token(/==/y).map(_ => Equal); - let NOT_EQUAL = token(/!=/y).map(_ => NotEqual); - let PLUS = token(/[+]/y).map(_ => Add); - let MINUS = token(/[-]/y).map(_ => Subtract); - let STAR = token(/[*]/y).map(_ => Multiply); - let SLASH = token(/[/]/y).map(_ => Divide); - // Expression parser - let expression: Parser = Parser.error('expression parser used before definition'); - // Call parser - let args: Parser> = expression.bind((arg: any) => Parser.zeroOrMore(COMMA.and(expression)).bind((args: any) => Parser.constant([arg, ...args]))).or(Parser.constant([])); - let functionCall: Parser = ID.bind((callee: any) => LEFT_PAREN.and(args.bind((args: any) => RIGHT_PAREN.and(Parser.constant(callee.equals(new Id('assert')) ? new Assert(args[0]) : new FunctionCall(callee, args)))))); - // Atom - let atom: Parser = functionCall.or(ID).or(NUMBER).or(LEFT_PAREN.and(expression).bind((e: any) => RIGHT_PAREN.and(Parser.constant(e)))); - // Unary operators - let unary: Parser = Parser.optional(NOT).bind((not: any) => atom.map((operand: any) => not ? new Not(operand) : operand)); - // Infix operators - let infix = (operatorParser: any, operandParser: any) => - operandParser.bind((operand: any) => - Parser.zeroOrMore( - operatorParser.bind((operator: any) => - operandParser.bind((operand: any) => - Parser.constant({ operator, operand }) - ) - ) - ).map((operatorTerms: any) => - operatorTerms.reduce((left: any, { operator, operand }: { operator: any, operand: any }) => - new operator(left, operand), operand) - ) - ); - let product = infix(STAR.or(SLASH), unary); - let sum = infix(PLUS.or(MINUS), product); - let comparison = infix(EQUAL.or(NOT_EQUAL), sum); - // Associativity - // Closing the loop: expression - expression.parse = comparison.parse; - // Statement - let statement: Parser = Parser.error('statement parser used before definition'); - let returnStatement: Parser = RETURN.and(expression).bind((operand: any) => SEMICOLON.and(Parser.constant(new Return(operand)))); - let expressionStatement: Parser = expression.bind((operand: any) => SEMICOLON.and(Parser.constant(operand))); - let ifStatement: Parser = IF.and(LEFT_PAREN).and(expression).bind((conditional: any) => - RIGHT_PAREN.and(statement).bind((consequence: any) => - ELSE.and(statement).bind((alternative: any) => - Parser.constant(new If(conditional, consequence, alternative)) - ) - ) - ); - let whileStatement: Parser = WHILE.and(LEFT_PAREN).and(expression).bind((conditional: any) => - RIGHT_PAREN.and(statement).bind((body: any) => - Parser.constant(new While(conditional, body)) - ) - ); - let varStatement: Parser = VAR.and(ID).bind((name: any) => - ASSIGN.and(expression).bind((value: any) => - SEMICOLON.and(Parser.constant(new Var(name, value))) - ) - ); - let assignmentStatement: Parser = ID.bind((name: any) => - ASSIGN.and(expression).bind((value: any) => - SEMICOLON.and(Parser.constant(new Assign(name, value))) - ) - ); - let blockStatement: Parser = LEFT_BRACE.and(Parser.zeroOrMore(statement)).bind((statements: any) => - RIGHT_BRACE.and(Parser.constant(new Block(statements))) - ); - let parameters: Parser> = ID.bind((param: any) => - Parser.zeroOrMore(COMMA.and(ID)).bind((params: any) => - Parser.constant([param, ...params]) - ) - ).or(Parser.constant([])); - let functionStatement: Parser = FUNCTION.and(ID).bind((name: any) => - LEFT_PAREN.and(parameters).bind((parameters: any) => - RIGHT_PAREN.and(blockStatement).bind((block: any) => - Parser.constant(name.equals(new Id('main')) ? new Main(block.statements) : new FunctionDefinition(name, parameters, block)) - ) - ) - ); - let statementParser: Parser = - returnStatement - .or(functionStatement) - .or(ifStatement) - .or(whileStatement) - .or(varStatement) - .or(assignmentStatement) - .or(blockStatement) - .or(expressionStatement); - statement.parse = statementParser.parse; - let parser: Parser = ignored.and(Parser.zeroOrMore(statement)).map((statements: any) => new Block(statements)); - - return parser.parseStringToCompletion(source); -} - -export class Parser { - constructor(public parse: (src: Source) => (ParseResult | null)) {} - - parseStringToCompletion(string: string): T { - let source = new Source(string, 0); - let result = this.parse(source); - if (!result) - throw Error('Parse error at index 0'); - let index = result.source.index; - if (index != result.source.string.length) - throw Error(`Parse error at index ${index}`); - return result.value; - } - - static regex(regex: RegExp): Parser { - return new Parser(source => source.match(regex)); - } - static constant(value: U): Parser { - return new Parser(source => new ParseResult(value, source)); - } - static error(message: string): Parser { - return new Parser(source => { - throw Error(message); - }) - } - static zeroOrMore(parser: Parser): Parser> { - return new Parser(source => { - let results = []; - let item: any; - while (item = parser.parse(source)) { - source = item.source; - results.push(item.value); - } - return new ParseResult(results, source); - }); - } - static optional(parser: Parser): Parser { - return parser.or(Parser.constant(null)); - } - - or(parser: Parser): Parser { - return new Parser(source => { - let result = this.parse(source); - return result ? result : parser.parse(source); - }); - } - bind(callback: (value: T) => Parser): Parser { - return new Parser(source => { - let result = this.parse(source); - if (result) { - let value = result.value; - let source = result.source; - return callback(value).parse(source); - } else return null; - }); - } - and(parser: Parser): Parser { - return this.bind(_ => parser); - } - map(callback: (t: T) => U): Parser { - return this.bind(value => Parser.constant(callback(value))) - } -} -export class Source { - constructor(public string: string, public index: number) {} - - match(regex: RegExp): (ParseResult | null) { - regex.lastIndex = this.index; - let match = this.string.match(regex); - if (match) { - let value = match[0]; - let newIndex = this.index + value.length; - let source = new Source(this.string, newIndex); - return new ParseResult(value, source); - } - return null; - } -} -export class ParseResult { - constructor(public value: T, public source: Source) {} -} diff --git a/tsconfig.json b/tsconfig.json deleted file mode 100644 index 217a628..0000000 --- a/tsconfig.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "compilerOptions": { - "strictNullChecks": false - }, - "include": ["src/**/*"], - "exclude": ["node_modules"] -}