Skip to content
This repository has been archived by the owner on Jun 18, 2023. It is now read-only.

Commit

Permalink
feat(core): nearley parser
Browse files Browse the repository at this point in the history
  • Loading branch information
prescientmoon committed Nov 8, 2020
1 parent 56f5f04 commit adb4c64
Show file tree
Hide file tree
Showing 9 changed files with 209 additions and 0 deletions.
3 changes: 3 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@
"hacky",
"idrc",
"iife",
"lparan",
"nearley",
"outdir",
"pcss",
"peaceiris",
Expand All @@ -22,6 +24,7 @@
"prebuild",
"purescript",
"purs",
"rparan",
"spago",
"thomashoneyman"
],
Expand Down
8 changes: 8 additions & 0 deletions config/webpack.common.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,14 @@ module.exports = {
options: {
loader: "ts"
}
},
{
test: /\.ne$/,
use: [
{
loader: "nearley-loader"
}
]
}
]
},
Expand Down
5 changes: 5 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
"devDependencies": {
"@semantic-release/changelog": "^5.0.1",
"@semantic-release/git": "^9.0.0",
"@types/moo": "^0.5.3",
"@types/nearley": "^2.11.1",
"autoprefixer": "^10.0.1",
"clean-webpack-plugin": "^3.0.0",
"compression-webpack-plugin": "^6.0.5",
Expand All @@ -22,6 +24,8 @@
"esbuild-loader": "^2.4.0",
"html-webpack-plugin": "^4.5.0",
"mini-css-extract-plugin": "^1.2.1",
"nearley": "^2.19.7",
"nearley-loader": "^2.0.0",
"optimize-css-assets-webpack-plugin": "^5.0.4",
"postcss": "^8.1.1",
"postcss-font-magician": "^2.3.1",
Expand All @@ -42,6 +46,7 @@
"@thi.ng/hiccup-canvas": "^1.1.10",
"@thi.ng/vectors": "^4.7.0",
"fibers": "^5.0.0",
"moo": "^0.5.1",
"preact": "^10.5.5",
"sass": "^1.29.0"
}
Expand Down
57 changes: 57 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

20 changes: 20 additions & 0 deletions src/ast.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// TODO: make this include position data.
type TermInput<T> = {
application: (left: T, right: T) => T
abstraction: (name: string, body: T) => T
variable: (name: string) => T
}

type Term<T> = (input: TermInput<T>) => T

export const call = <T>(left: Term<T>, right: Term<T>): Term<T> => (config) =>
config.application(left(config), right(config))

export const abstract = <T>(name: string, body: Term<T>): Term<T> => (config) =>
config.abstraction(name, body(config))

export const variable = <T>(name: string): Term<T> => (config) =>
config.variable(name)

export const abstractMany = <T>(names: string[], body: Term<T>): Term<T> =>
names.reduceRight((previous, name) => abstract(name, previous), body)
2 changes: 2 additions & 0 deletions src/entry.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
console.log("Loaded root")
console.time("Loaded purescript")

import "./parser.ts"

import("purescript/Main").then(({ main }) => {
console.timeEnd("Loaded purescript")

Expand Down
58 changes: 58 additions & 0 deletions src/lexer.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import moo from "moo"

export interface Position {
line: number
col: number
}

export interface Token {
start: Position
end: Position
type?: string
value: string
}

export const lexer = moo.compile({
ws: /[ \t]+/,
nl: { match: "\n", lineBreaks: true },
lambdaChar: ["λ", "\\"],
arrowChar: ["->", "."],
lparan: "(",
rparan: ")",
identifier: {
match: /[a-z_][a-z_0-9]*/
}
})

export function tokenStart(token: moo.Token): Position {
return {
line: token.line,
col: token.col - 1
}
}

export function tokenEnd(token: moo.Token): Position {
const lastNewLine = token.text.lastIndexOf("\n")

if (lastNewLine !== -1) {
throw new Error("Unsupported case: token with line breaks")
}

return {
line: token.line,
col: token.col + token.text.length - 1
}
}

export function convertToken(token: moo.Token): Token {
return {
type: token.type,
value: token.value,
start: tokenStart(token),
end: tokenEnd(token)
}
}

export function convertTokenId(data: moo.Token[]): Token {
return convertToken(data[0])
}
14 changes: 14 additions & 0 deletions src/parser.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// @ts-ignore
import syntax from "./syntax.ne"
import { Parser, Grammar } from "nearley"

const parser = new Parser(Grammar.fromCompiled(syntax))

parser.feed("\\f a b. f b a \\l. l")
const result = parser.results[0]({
variable: (d) => d,
abstraction: (name, body) => ({ name, body }),
application: (left, right) => ({ left, right })
})

console.log(result)
42 changes: 42 additions & 0 deletions src/syntax.ne
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
@{%
const { lexer, tokenStart, convertTokenId } = require("./lexer.ts")
const { call, abstract, variable, abstractMany } = require("./ast.ts")
%}

@lexer lexer

expression
-> application {% id %}
| abstraction {% id %}

application
-> no_lambda_application {% id %}
| no_lambda_application __ abstraction
{% ([left, _, right]) => call(left, right) %}

no_lambda_application
-> atom {% id %}
| no_lambda_application __ atom
{% ([left, _, right]) => call(left, right) %}


atom
-> "(" _ expression _ ")" {% d => d[2] %}
| variable {% id %}

variable
-> identifier
{% ([d]) => variable(d.value) %}

abstraction
-> lambdaChar lambdaArgs _ arrowChar _ expression
{% d => abstractMany(d[1].map(a => a.value), d[5]) %}

lambdaArgs -> (_ identifier _ {% d => d[1] %}):+ {% id %}

# Lexing
arrowChar -> %arrowChar {% convertTokenId %}
lambdaChar -> %lambdaChar {% convertTokenId %}
identifier -> %identifier {% convertTokenId %}
__ -> %ws:+
_ -> %ws:*

0 comments on commit adb4c64

Please sign in to comment.