Skip to content

Commit

Permalink
Merge pull request #111 from kyleect/parse-command
Browse files Browse the repository at this point in the history
Add parse command
  • Loading branch information
kyleect authored Dec 9, 2023
2 parents 831003f + f94b747 commit 23bd7f6
Show file tree
Hide file tree
Showing 13 changed files with 325 additions and 22 deletions.
88 changes: 85 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ With the syntax and implementation changes so far the Locks language has divered
- Comments added to code base as part of the learning process while implementing changes
- Rewrote & decoupled disassembler to build a string of the disassembled bytecode instead of printing it
- CLI enhancements
- Add `parse` command to print the AST from a `*.locks` file.
- Add `dissassemble` command to print disassembled bytecode from a `*.locks` file
- Add `exec` command to execute Locks code from the arg or piped in from `stdin`
- Syntax changes
Expand All @@ -51,7 +52,7 @@ With the syntax and implementation changes so far the Locks language has divered
- Integrates the existing [language server](src/lsp.rs) to display parsing/compiler errors
- Syntax Highlighting, Auto Pair Complete
- Snippets
- Commands/tasks to run, and disassemble Locks code
- Commands/tasks to run, parse, and disassemble Locks code
- Debug config for running VS Code Extension in VS Code
- Add builds (Locks binary executable & VS Code extension) as artifacts to the Github workflow
- Revamped the [Online Playground](https://kyleect.github.io/locks/)
Expand Down Expand Up @@ -116,6 +117,86 @@ Or from stdin
$ cat res/benchmarks/fib.locks | locks exec
```

Parse and print AST

```
// example.locks
let value;
print value; // out: nil
value = 42;
print value; // out: 42;
```

```shell
$ locks parse example.locks
```

```
Program {
stmts: [
(
StmtAssign {
identifier: Identifier {
name: "value",
depth: None,
},
value: None,
},
72..82,
),
(
StmtPrint {
value: (
ExprIdentifier {
identifier: Identifier {
name: "value",
depth: None,
},
},
89..94,
),
},
83..95,
),
(
StmtExpr {
value: (
ExprAssign {
identifier: Identifier {
name: "value",
depth: None,
},
value: (
Number(
42.0,
),
116..118,
),
},
108..118,
),
},
108..119,
),
(
StmtPrint {
value: (
ExprIdentifier {
identifier: Identifier {
name: "value",
depth: None,
},
},
126..131,
),
},
120..132,
),
],
}
```

Disassemble locks code as visualized byte code

```
Expand Down Expand Up @@ -231,8 +312,9 @@ Download the VS Code extension from the [latest build](https://github.com/kyleec

#### Features

- Language Server support
- Syntax highlighting
- Language Server integration
- Syntax & error highlighting
- Commands
- Snippets

## Development
Expand Down
31 changes: 31 additions & 0 deletions playground/rust/lox-wasm/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,37 @@ pub fn locksDisassemble(source: &str) {
}
}

#[wasm_bindgen]
#[allow(non_snake_case)]
pub fn locksParse(source: &str) {
console_error_panic_hook::set_once();

let output = &mut Output::new();

let program = match parse(source, source.len()) {
Ok(program) => program,
Err(errors) => {
let mut writer = HtmlWriter::new(output);
for e in errors.iter() {
report_error(&mut writer, source, e);
}
postMessage(&Message::ExitFailure.to_string());

return;
}
};

let result = format!("{:#?}", program);

let result = result.replace(" ", " ");

let encoded_result = askama_escape::escape(&result, askama_escape::Html).to_string();

let _ = output.write(encoded_result.as_bytes());

postMessage(&Message::ExitSuccess.to_string());
}

#[allow(dead_code)]
#[derive(Debug, Serialize)]
#[serde(tag = "type")]
Expand Down
17 changes: 15 additions & 2 deletions playground/src/components/doc-card/DocCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
import { useLocks } from '../../hooks/useLocks';
import { Example } from '../example';
import { LocksDisassembleButton } from '../locks-disassemble-button';
import { LocksParseButton } from '../locks-parse-button';
import { LocksRunButton } from '../locks-run-button';
import { Output } from '../output';

Expand All @@ -24,8 +25,14 @@ const DocCard: React.FC<DocCardProps> = ({
anchor,
children,
}) => {
const { isRunning, runLocks, disassembleLocks, stopLocks, locksResult } =
useLocks();
const {
isRunning,
runLocks,
disassembleLocks,
parseLocks,
stopLocks,
locksResult,
} = useLocks();
const value = Array.isArray(code) ? code.join('\n') : code;

return (
Expand All @@ -52,6 +59,12 @@ const DocCard: React.FC<DocCardProps> = ({
onClick={isRunning ? stopLocks : () => runLocks(value)}
/>

<LocksParseButton
className="ms-2"
isRunning={isRunning}
onClick={isRunning ? stopLocks : () => parseLocks(value)}
/>

<LocksDisassembleButton
className="ms-2"
isRunning={isRunning}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ const LocksDisassembleButton: React.FC<LocksDisassembleButtonProp> = ({
className,
}) => {
let runColor = 'btn-secondary';
let runIcon = 'me-1 bi bi-columns-gap';
let runIcon = 'me-1 bi bi-file-earmark-binary-fill';
let runText = 'Disassemble';

if (isRunning) {
Expand Down
43 changes: 43 additions & 0 deletions playground/src/components/locks-parse-button/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
/* eslint-disable @typescript-eslint/restrict-template-expressions */
import React from 'react';

interface LocksParseButtonProps {
isRunning: boolean;
onClick: () => void;
className?: string;
}

const LocksParseButton: React.FC<LocksParseButtonProps> = ({
isRunning,
onClick,
className,
}) => {
let runColor = 'btn-secondary';
let runIcon = 'me-1 bi bi-file-earmark-code-fill';
let runText = 'Parse';

if (isRunning) {
runColor = 'btn-danger';
runIcon = 'me-2 spinner-grow spinner-grow-sm';
runText = 'Stop';
}

return (
<button
id="run-btn"
className={`btn ${runColor} ${className}`}
onClick={onClick}
type="button"
aria-label="Run code"
>
<span className={runIcon} role="status" aria-hidden="true" />
<span className="d-none d-lg-inline">{runText}</span>
</button>
);
};

LocksParseButton.defaultProps = {
className: '',
};

export { LocksParseButtonProps, LocksParseButton };
36 changes: 36 additions & 0 deletions playground/src/hooks/useLocks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,41 @@ export function useLocks() {
setWorker(webWorker);
};

const parseLocks = (code: string) => {
stopWorker();
setLocksResult(null);

const webWorker = new Worker(new URL('../worker.ts', import.meta.url), {
type: 'module',
});

webWorker.onmessage = (event) => {
const msg: LoxOutMessage = JSON.parse(
event.data as string,
) as LoxOutMessage;

switch (msg.type) {
case 'Output':
appendToLocksResult(msg.text);
break;
case 'Diagnostics':
setDiagnostics(msg.diagnostics);
break;
case 'ExitSuccess':
stopWorker();
break;
case 'ExitFailure':
stopWorker();
break;
default:
break;
}
};

webWorker.postMessage({ code, action: 'parse' });
setWorker(webWorker);
};

const diagnoseLocks = (code: string) => {
stopWorker();
setLocksResult(null);
Expand Down Expand Up @@ -188,6 +223,7 @@ export function useLocks() {
locksResult,
runLocks,
disassembleLocks,
parseLocks,
stopLocks,
diagnostics,
diagnoseLocks,
Expand Down
5 changes: 5 additions & 0 deletions playground/src/pages/docs.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -913,6 +913,11 @@ const Docs: React.FC = () => (
<pre>$ locks exec &squo;print &quot;Hello&quot;;&squo;</pre>
<pre>$ cat res/benchmarks/fib.locks | locks exec</pre>
</div>
<div className="shadow rounded p-3">
<h4>Parse File</h4>
Visualize AST from file
<pre>$ locks parse ./res/examples/number/fizzbuzz.locks</pre>
</div>
<div className="shadow rounded p-3">
<h4>Disassemble File</h4>
Visualize compiled byte code from file
Expand Down
38 changes: 26 additions & 12 deletions playground/src/pages/playground.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {

import { Editor } from '../components/editor';
import { LocksDisassembleButton } from '../components/locks-disassemble-button';
import { LocksParseButton } from '../components/locks-parse-button';
import { LocksRunButton } from '../components/locks-run-button';
import { Navbar } from '../components/navbar';
import { Output } from '../components/output';
Expand All @@ -35,8 +36,14 @@ class LocalStorage {
const Playground: React.FC = () => {
const [searchParams] = useSearchParams();
const navigate = useNavigate();
const { isRunning, runLocks, disassembleLocks, stopLocks, locksResult } =
useLocks();
const {
isRunning,
runLocks,
disassembleLocks,
parseLocks,
stopLocks,
locksResult,
} = useLocks();

/**
* @remarks
Expand Down Expand Up @@ -135,16 +142,23 @@ const Playground: React.FC = () => {
aria-hidden="true"
/>
</button>
<LocksDisassembleButton
isRunning={isRunning}
onClick={
isRunning ? stopLocks : () => disassembleLocks(editorText)
}
/>
<LocksRunButton
isRunning={isRunning}
onClick={isRunning ? stopLocks : () => runLocks(editorText)}
/>

<div className="btn-group" role="group" aria-label="Basic example">
<LocksParseButton
isRunning={isRunning}
onClick={isRunning ? stopLocks : () => parseLocks(editorText)}
/>
<LocksDisassembleButton
isRunning={isRunning}
onClick={
isRunning ? stopLocks : () => disassembleLocks(editorText)
}
/>
<LocksRunButton
isRunning={isRunning}
onClick={isRunning ? stopLocks : () => runLocks(editorText)}
/>
</div>
</>
}
/>
Expand Down
11 changes: 10 additions & 1 deletion playground/src/worker.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
import init, { loxRun, locksDisassemble, loxDiagnose } from 'lox-wasm';
import init, {
loxRun,
locksDisassemble,
loxDiagnose,
locksParse,
} from 'lox-wasm';

onmessage = async (event) => {
await init();
Expand All @@ -15,6 +20,10 @@ onmessage = async (event) => {
locksDisassemble(code as string);
break;

case 'parse':
locksParse(code as string);
break;

case 'diagnose':
loxDiagnose(code as string);
break;
Expand Down
Loading

0 comments on commit 23bd7f6

Please sign in to comment.