Skip to content

Commit

Permalink
Rewrite 'ursa fmt' using Topiary
Browse files Browse the repository at this point in the history
Temporarily comment out the test test/bad-assignment, as the current
tree-sitter grammar does not accept e.g. “2 := 3”. I think this is good! and
will make the Ohm grammar refuse it as well in a follow-up commit.
  • Loading branch information
rrthomas committed Sep 10, 2024
1 parent a59827e commit 188c9d6
Show file tree
Hide file tree
Showing 9 changed files with 184 additions and 667 deletions.
6 changes: 3 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
"argparse": "^2.0.1",
"effection": "^3.0.3",
"env-paths": "^3.0.0",
"execa": "^9.3.0",
"fs-extra": "^11.2.0",
"get-source": "^2.0.12",
"ohm-js": "^17.1.0",
Expand All @@ -43,7 +44,6 @@
"dir-compare": "^5.0.0",
"eslint-config-airbnb-base": "^15.0.0",
"eslint-config-airbnb-typescript": "^18.0.0",
"execa": "^9.3.0",
"pre-push": "^0.1.4",
"tree-kill": "^1.2.2",
"ts-node": "^10.9.2",
Expand All @@ -63,7 +63,7 @@
"scripts": {
"lint": "eslint . --ext .ts && ts-unused-exports ./tsconfig.json --ignoreFiles=\"./src/grammar/ursa.ohm-bundle.*.ts\" && depcheck",
"prebuild": "node --print \"'export default \\'' + require('./package.json').version + '\\';'\" > src/version.ts && npm run pre-compile-prelude",
"build": "tsc --build && npm run compile-prelude && mkdir -p lib/ark/compiler lib/ursa && cp src/ark/prelude.json lib/ark/ && cp src/ark/compiler/prelude.js lib/ark/compiler/ && cp src/ursa/prelude.ursa lib/ursa/",
"build": "tsc --build && npm run compile-prelude && mkdir -p lib/ark/compiler lib/ursa lib/topiary && cp src/ark/prelude.json lib/ark/ && cp src/ark/compiler/prelude.js lib/ark/compiler/ && cp src/ursa/prelude.ursa lib/ursa/ && cp src/topiary/ursa.scm lib/topiary/",
"clean": "tsc --build --clean && rm -f src/grammar/ursa.ohm-bundle.ts src/grammar/ursa.ohm-bundle.d.ts",
"generate-only": "ohm generateBundles --withTypes --esm 'src/grammar/*.ohm'",
"patch-diff-ohm": "patch -p0 --output=src/grammar/ursa.ohm-bundle.d.part-patched.ts < src/grammar/ursa.ohm-bundle.d.ts.diff",
Expand Down Expand Up @@ -92,7 +92,7 @@
},
"compile": "tsc"
},
"timeout": "30s",
"timeout": "60s",
"workerThreads": false
},
"pre-push": [
Expand Down
128 changes: 128 additions & 0 deletions src/topiary/ursa.scm
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
; Topiary queries for Ursa.

; Sometimes we want to indicate that certain parts of our source text should
; not be formatted, but taken as-is. We use the leaf capture name to inform the
; tool of this.
[
(raw_string_literal)
(string)
] @leaf

; Allow blank line before
[
(line_comment)
(block_comment)
(statement)
(member)
] @allow_blank_line_before

; Surround spaces
[
"and"
"else"
"in"
"or"
"="
":="
] @prepend_space @append_space

(binary_exp (_) _ @prepend_space @append_space (_))

; Append spaces
[
"await"
"break"
(continue)
"for"
"if"
"launch"
"let"
"loop"
"not"
"return"
"use"
"var"
"yield"
":"
] @append_space

; Input softlines before all comments. This means that the input decides if
; a comment should have line breaks before. A line comment always ends with
; a line break.
[
(block_comment)
(line_comment)
"else"
] @prepend_input_softline

; Input softline after block comments unless followed by comma or semicolon, as
; they are always put directly after.
(
(block_comment) @append_input_softline
.
["," ";"]* @do_nothing
)

; Put on a separate line. If there is a comment following, we don't add anything,
; because the input softlines and spaces above will already have sorted out the
; formatting.
(
[
(statement)
(member)
] @prepend_input_softline @append_input_softline
)

(line_comment) @append_hardline

(block_comment) @multi_line_indent_all

; Append softlines, unless followed by comments.
(
[
","
";"
] @append_spaced_softline
.
[(block_comment) (line_comment)]* @do_nothing
)

; Prepend softlines before dots
(_
"." @prepend_empty_softline
)

; This patterns is duplicated for all nodes that can contain curly braces.
; Hoping to be able to generalise them like this:
; (_
; .
; "{" @prepend_space
; (#for! block declaration_list enum_variant_list field_declaration_list)
; )
; Perhaps even the built in #match! can do this

;; fn
; (fn
; (identifier) @prepend_space
; )

(block
.
"{" @prepend_space
)

(block
.
"{" @append_spaced_softline @append_indent_start
_
"}" @prepend_spaced_softline @prepend_indent_end
.
)

(object
.
"{" @append_spaced_softline @append_indent_start
_
"}" @prepend_spaced_softline @prepend_indent_end
.
)
18 changes: 9 additions & 9 deletions src/ursa/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,12 @@

import assert from 'assert'
import path from 'path'
import fs, {PathOrFileDescriptor} from 'fs-extra'
import * as readline from 'readline'
import {fileURLToPath} from 'url'

import {ArgumentParser, RawDescriptionHelpFormatter} from 'argparse'
import envPaths from 'env-paths'
import fs, {PathOrFileDescriptor} from 'fs-extra'
import tildify from 'tildify'
import {rollup} from 'rollup'
import {nodeResolve} from '@rollup/plugin-node-resolve'
Expand All @@ -34,6 +34,9 @@ import {
} from '../ark/compiler/index.js'
import {expToInst} from '../ark/flatten.js'

// eslint-disable-next-line @typescript-eslint/naming-convention
const __dirname = fileURLToPath(new URL('.', import.meta.url))

if (process.env.DEBUG) {
Error.stackTraceLimit = Infinity
}
Expand Down Expand Up @@ -98,9 +101,7 @@ const fmtParser = subparsers.add_parser('fmt', {aliases: ['f', 'format'], descri
fmtParser.set_defaults({func: fmtCommand})
fmtParser.add_argument('source', {metavar: 'FILE', help: 'source code to format'})
fmtParser.add_argument('--output', '-o', {metavar: 'FILE', help: 'output file [default: standard output]'})
fmtParser.add_argument('--width', {metavar: 'COLUMNS', help: 'maximum desired width of formatted code'})
fmtParser.add_argument('--indent', {metavar: 'STRING', help: 'indent string'})
fmtParser.add_argument('--onelineFactor', {metavar: 'NUMBER', help: 'factor governing when expressions are wrapped (bigger means try to fit more complex expressions on one line) [default: 0]'})

interface Args {
// Global arguments
Expand Down Expand Up @@ -142,7 +143,7 @@ function getOutputFile(
let prog: string

// Use standard input if requested
function getInputFile(args: Args) {
function getInputFile(args: Args): PathOrFileDescriptor {
let inputFile: PathOrFileDescriptor = args.source
if (args.source !== '-') {
prog = inputFile
Expand Down Expand Up @@ -363,8 +364,6 @@ async function compileCommand(args: Args) {
for (const k of recordKeys<string, unknown>(runtimeContext)) {
names.push(k)
}
// eslint-disable-next-line @typescript-eslint/naming-convention
const __dirname = fileURLToPath(new URL('.', import.meta.url))
output += `import {${names.join(', ')}} from '${path.join(__dirname, '../../lib/ark/data.js')}'\n`
output += `import {run, spawn} from '${path.join(__dirname, '../../node_modules/effection/esm/mod.js')}'\nlet prelude = ${prelude};
(await (run(prelude))).properties.forEach((val, sym) => jsGlobals.set(sym, val))\n`
Expand All @@ -389,11 +388,12 @@ async function compileCommand(args: Args) {
}

function fmtCommand(args: Args) {
const outputFile = getOutputFile(args, true)
const inputFile = getInputFile(args)
const source = readSourceFile(inputFile)
const output = format(source, args.width, args.indent, args.onelineFactor)
const input = fs.readFileSync(inputFile, {encoding: 'utf-8'})
const output = format(input, args.indent)
const outputFile = getOutputFile(args, true)
fs.writeFileSync(outputFile, output)
delete process.env.TOPIARY_LANGUAGE_DIR
return Promise.resolve()
}

Expand Down
2 changes: 1 addition & 1 deletion src/ursa/examples.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ import {
['Test error on bad function call', 'test/bad-call'],
['Test error on invalid method invocation', 'test/sum-map-iterator-invalid-method'],
['Test error on invalid property access', 'test/sum-map-iterator-invalid-property'],
['Test error on assignment to non-lvalue', 'test/bad-assignment'],
//['Test error on assignment to non-lvalue', 'test/bad-assignment'],
['Test error on re-assignment with wrong type', 'test/bad-reassignment'],
["Test error on duplicate identifiers in 'let'", 'test/duplicate-let'],
["Test error on 'yield' outside function", 'test/bad-yield'],
Expand Down
Loading

0 comments on commit 188c9d6

Please sign in to comment.