diff --git a/docs/specification/characters_letters_digits.md b/docs/specification/characters_letters_digits.md index 5e7701d..6d7ac86 100644 --- a/docs/specification/characters_letters_digits.md +++ b/docs/specification/characters_letters_digits.md @@ -1,7 +1,7 @@ -There are a few predefined rules that could not easily be written as valid -EBNF or are used multiple times and therefor defined here to avoid repeating -them. The EBNF rules for the identifiers `unicode_*` are written within -comments as there is no real way to define them otherways. +There are a few predefined rules that could not easily be written as valid EBNF +or are used multiple times and therefor defined here to avoid repeating them. +The EBNF rules for the identifiers `unicode_*` are written within comments as +there is no real way to define them otherways. ```EBNF unicode_any = // any Unicode code point diff --git a/docs/specification/expressions.md b/docs/specification/expressions.md index c83eb16..6a6fdf0 100644 --- a/docs/specification/expressions.md +++ b/docs/specification/expressions.md @@ -1,16 +1,16 @@ -An expression in _Whistle_ evaluates to a value, this could be through either [binary](#binary) or -[unary expressions](#unary), or the special [conditional expression](#conditional). +An expression in _Whistle_ evaluates to a value, this could be through either +[binary](#binary) or [unary expressions](#unary), or the special +[conditional expression](#conditional). -> In the future, once [PR #21](https://github.com/whistle-lang/whistle/pull/21) gets merged, there -> will be a new type of expression: that of the array slice and index accessors. +> In the future, once [PR #21](https://github.com/whistle-lang/whistle/pull/21) +> gets merged, there will be a new type of expression: that of the array slice +> and index accessors. ## Unary A unary expression can be one of two things, either a primary or an operation. - -### Primary - -> TODO, Probably move this to a new file +Due to the complexity and many different types of primary expressions the +specification for them can be found [here](./primary_expressions). ### Operation diff --git a/docs/specification/literals.md b/docs/specification/literals.md index 4df66b3..4469f0e 100644 --- a/docs/specification/literals.md +++ b/docs/specification/literals.md @@ -1,8 +1,8 @@ -Literals are one of the essential parts of any program, they express values and one of the possible -building blocks of an [expression](./expressions). +Literals are one of the essential parts of any program, they express values and +one of the possible building blocks of an [expression](./expressions). -In _Whistle_ there are six literal types: booleans, integers, floats, characters, strings and the -none literal. +In _Whistle_ there are six literal types: booleans, integers, floats, +characters, strings and the none literal. ```EBNF literal = bool_literal @@ -15,7 +15,8 @@ literal = bool_literal ## Booleans -Booleans represent a true or false value, it does this through the keywords `true` and `false`. +Booleans represent a true or false value, it does this through the keywords +`true` and `false`. ```EBNF bool_literal = "true" | "false" @@ -23,10 +24,10 @@ bool_literal = "true" | "false" ## Integers -Integers represent a whole number in either binary, octal, decimal or hexadecimal base. Integer -literals are always positive/unsigned numbers, but this does not mean _Whistle_ only supports -unsigned integers, instead to use signed integers one would use the [negate](./operators#unary) -operator. +Integers represent a whole number in either binary, octal, decimal or +hexadecimal base. Integer literals are always positive/unsigned numbers, but +this does not mean _Whistle_ only supports unsigned integers, instead to use +signed integers one would use the [negate](./operators#unary) operator. ```EBNF int_literal = int_literal_binary @@ -41,10 +42,11 @@ int_literal_hex = "0" , ( "x" | "X" ) , { digit_hex } ## Floating point numbers -The float literal represents an [ieee754](https://en.wikipedia.org/wiki/IEEE_754) floating point -number. This number can contain an optional fractional part and or exponent along with the whole -part. Once again to negate the float literal one would use the [negate](./operators#unary) -operator. +The float literal represents an +[ieee754](https://en.wikipedia.org/wiki/IEEE_754) floating point number. This +number can contain an optional fractional part and or exponent along with the +whole part. Once again to negate the float literal one would use the +[negate](./operators#unary) operator. ```EBNF float_literal = { digit_decimal } , [ float_decimal ] , [ float_exponent ] @@ -54,9 +56,10 @@ float_exponent = ( "e" | "E" ) , [ "+" | "-" ] , { digit_decimal } ## Characters and strings -The character literal represents a single unicode character while a string represents an sequence -of these unicode characters. There are certain escaped values for things like newlines, tabs and -null bytes. These escaped values apply for both the inner values of strings and characters. +The character literal represents a single unicode character while a string +represents an sequence of these unicode characters. There are certain escaped +values for things like newlines, tabs and null bytes. These escaped values apply +for both the inner values of strings and characters. ```EBNF escaped_value = "\" , (""" | "\" | "r" | "n" | "t" | "0" | "'") diff --git a/docs/specification/primary_expressions.md b/docs/specification/primary_expressions.md new file mode 100644 index 0000000..e69de29 diff --git a/docs/toc.json b/docs/toc.json index 8f1887a..142701b 100644 --- a/docs/toc.json +++ b/docs/toc.json @@ -13,7 +13,8 @@ "title": "Characters, letters and digits" }, "literals": { "title": "Literals" }, - "expressions": { "title": "Expressions" } + "expressions": { "title": "Expressions" }, + "primary_expressions": { "title": "Primary Expressions" } } } } diff --git a/www/client_deps.ts b/www/client_deps.ts index 5063c36..64cf208 100644 --- a/www/client_deps.ts +++ b/www/client_deps.ts @@ -1,7 +1,23 @@ -export * from "https://raw.githubusercontent.com/lucacasonato/fresh/main/runtime.ts"; import { IS_BROWSER } from "https://raw.githubusercontent.com/lucacasonato/fresh/main/runtime.ts"; -import { apply, setup, tw } from "https://esm.sh/twind@0.16.16"; +import { + apply, + Plugin, + setup, + ThemeConfiguration, + tw, +} from "https://esm.sh/twind@0.16.16"; +import * as config from "./tw_config.ts"; + +export * as colors from "https://cdn.esm.sh/v73/twind@0.16.16/es2021/colors.js"; +export * from "https://raw.githubusercontent.com/whistle-lang/whistle_deno/master/mod.ts"; +export * from "https://raw.githubusercontent.com/lucacasonato/fresh/main/runtime.ts"; +export { default as wabt } from "https://esm.sh/wabt@1.0.28?target=esnext"; +export { default as MonacoEditor } from "https://esm.sh/@monaco-editor/react@4.3.1?alias=react:@preact/compat,react-dom:@preact/compat"; +export type { Monaco } from "https://esm.sh/@monaco-editor/react@4.3.1?alias=react:@preact/compat,react-dom:@preact/compat"; +export type { MutableRef } from "https://cdn.esm.sh/v73/preact@10.6.6/hooks/src/index"; export { apply, setup, tw }; +export type { Plugin, ThemeConfiguration }; + if (IS_BROWSER) { - setup({}); + setup(config); } diff --git a/www/components/Footer.tsx b/www/components/Footer.tsx index 53ce86b..ee74954 100644 --- a/www/components/Footer.tsx +++ b/www/components/Footer.tsx @@ -7,6 +7,10 @@ const LINKS = [ title: "Source", href: "https://github.com/whistle-lang", }, + { + title: "Discord", + href: "https://discord.gg/hdKxd5x" + } ]; export default function Footer() { diff --git a/www/components/Header.tsx b/www/components/Header.tsx new file mode 100644 index 0000000..04fb29c --- /dev/null +++ b/www/components/Header.tsx @@ -0,0 +1,15 @@ +/** @jsx h */ +/** @jsxFrag Fragment */ + +import { h, tw } from "../client_deps.ts"; +import { IconWhistle } from "./Icons.tsx"; + +export default function Header() { + return ( +
+
+ +
+
+ ); +} diff --git a/www/components/Icons.tsx b/www/components/Icons.tsx index f2fe615..2e98a11 100644 --- a/www/components/Icons.tsx +++ b/www/components/Icons.tsx @@ -2,42 +2,26 @@ import { h, tw } from "../client_deps.ts"; -export function IconMinus() { +export function IconWhistle() { return ( - +

+ + + +

); } -export function IconPlus() { +export function IconChevron({ ...props }) { return ( ); } diff --git a/www/components/NavigationBar.tsx b/www/components/NavigationBar.tsx index 59e7f68..5df4ed8 100644 --- a/www/components/NavigationBar.tsx +++ b/www/components/NavigationBar.tsx @@ -16,10 +16,10 @@ export default function NavigationBar(props: { active: string }) { name: "Blog", href: "/blog", }, - // { - // name: "Playground", - // href: "/playground", - // }, + { + name: "Playground", + href: "/playground", + }, ]; return ( diff --git a/www/data/playground.ts b/www/data/playground.ts new file mode 100644 index 0000000..a81fba6 --- /dev/null +++ b/www/data/playground.ts @@ -0,0 +1,139 @@ +export const WhistleLanguageDef = { + keywords: [ + "import", + "as", + "from", + "export", + "fn", + "return", + "if", + "else", + "while", + "break", + "continue", + "var", + "val", + "none", + "for", + "in", + "match", + "type", + "struct", + "trait", + ], + + typeKeywords: [ + "none", + "str", + "bool", + "i8", + "i16", + "i32", + "i64", + "u8", + "u16", + "u32", + "u64", + "f32", + "f64", + ], + + operators: [ + "&&=", + "||=", + "&&", + "||", + "!", + "+=", + "-=", + "*=", + "/=", + "%=", + "**=", + "+", + "-", + "*", + "/", + "%", + "**", + "<<=", + ">>=", + "<<", + ">>", + "&=", + "|=", + "^=", + "&", + "|", + "^", + "~", + "==", + "!=", + "<=", + ">=", + "<", + ">", + "=", + ], + + symbols: /[=>](?!@symbols)/, "@brackets"], + [/@symbols/, { + cases: { + "@operators": "operator", + "@default": "", + }, + }], + + [/\d*\.\d+([eE][\-+]?\d+)?/, "number.float"], + [/0[xX][0-9a-fA-F]+/, "number.hex"], + [/\d+/, "number"], + + [/[;,.]/, "delimiter"], + + [/"([^"\\]|\\.)*$/, "string.invalid"], + [/"/, { token: "string.quote", bracket: "@open", next: "@string" }], + + [/'[^\\']'/, "string"], + [/(')(@escapes)(')/, ["string", "string.escape", "string"]], + [/'/, "string.invalid"], + ], + + comment: [ + [/[^\/*]+/, "comment"], + [/\/\*/, "comment", "@push"], + ["\\*/", "comment", "@pop"], + [/[\/*]/, "comment"], + ], + + string: [ + [/[^\\"]+/, "string"], + [/@escapes/, "string.escape"], + [/\\./, "string.escape.invalid"], + [/"/, { token: "string.quote", bracket: "@close", next: "@pop" }], + ], + + whitespace: [ + [/[ \t\r\n]+/, "white"], + [/\/\*/, "comment", "@comment"], + [/\/\/.*$/, "comment"], + ], + }, +}; diff --git a/www/fresh.gen.ts b/www/fresh.gen.ts index 0fa8ad6..e444fe8 100644 --- a/www/fresh.gen.ts +++ b/www/fresh.gen.ts @@ -8,6 +8,8 @@ import * as $2 from "./routes/blog/index.tsx"; import * as $3 from "./routes/docs/[...slug].tsx"; import * as $4 from "./routes/gfm.css.ts"; import * as $5 from "./routes/index.tsx"; +import * as $6 from "./routes/playground.tsx"; +import * as $$0 from "./islands/Playground.tsx"; const manifest = { routes: { @@ -17,8 +19,11 @@ const manifest = { "./routes/docs/[...slug].tsx": $3, "./routes/gfm.css.ts": $4, "./routes/index.tsx": $5, + "./routes/playground.tsx": $6, + }, + islands: { + "./islands/Playground.tsx": $$0, }, - islands: {}, baseUrl: import.meta.url, }; diff --git a/www/islands/Playground.tsx b/www/islands/Playground.tsx new file mode 100644 index 0000000..6b3204b --- /dev/null +++ b/www/islands/Playground.tsx @@ -0,0 +1,193 @@ +/** @jsx h */ +/** @jsxFrag Fragment */ + +import { + Component, + Fragment, + h, + load, + Monaco, + MonacoEditor, + Ref, + tw, + useEffect, + useRef, + useState, + wabt as loadWabt, +} from "../client_deps.ts"; +import { IconChevron } from "../components/Icons.tsx"; +import { WhistleLanguageDef } from "../data/playground.ts"; + +export default class Playground extends Component { + // deno-lint-ignore no-explicit-any + wabt!: any; + + async componentDidMount() { + await load(); + const wabt = await loadWabt(); + + this.setState({ wabt }); + } + + run() { + } + + // deno-lint-ignore no-empty-pattern no-explicit-any + render({}, { wabt }: { wabt: any }) { + const options = [ + { + name: "WebAssembly", + display: () => { + if (textRef.current) { + textRef.current.innerText = "wasm2wat"; + } + }, + }, + { + name: "Lexer Tokens", + display: () => { + if (textRef.current) { + textRef.current.innerText = "tokens"; + } + }, + }, + { + name: "Abstract Syntax Tree", + display: () => { + if (textRef.current) { + textRef.current.innerText = "ast"; + } + }, + }, + ]; + const editorRef = useRef(null); + const textRef = useRef(null); + const logsRef = useRef(null); + + return ( +
+
    + Run +
    + option.display()} + /> +
    +
+ +
+ ); + } +} + +function TopBarButton( + { children, onClick }: { + children: string; + onClick?: h.JSX.MouseEventHandler; + }, +) { + return ( +
  • + {children} +
  • + ); +} + +function TopBarDropdown( + { options, onSelected }: { + options: T; + onSelected?: ( + option: { [P in keyof T]: T[P] } extends { [key: number]: infer V } ? V + : never, + ) => void; + }, +) { + const [selected, setSelected] = useState(options[0]); + + const dropdown = useRef(null); + const toggleDropdown = () => { + if (dropdown.current) { + dropdown.current.hidden = !dropdown.current.hidden; + } + }; + + return ( +
    +
    + {selected.name} + +
    + + +
    + ); +} + +function Panels({ monacoRef, textRef, logsRef }: { monacoRef?: MutableRef; textRef?: Ref; logsRef?: Ref }) { + // deno-lint-ignore no-explicit-any + const handleEditorWillMount = (monaco: any) => { + monaco.languages.register({ + id: "whistle", + }); + monaco.languages.setMonarchTokensProvider("whistle", WhistleLanguageDef); + }; + // deno-lint-ignore no-explicit-any + const handleEditorDidMount = (editor: any, monaco: any) => { + monacoRef.current = editor; + }; + return ( +
    +
    + +
    + +
    +
    +
    + +
    +
    +
    +
    + ); +} diff --git a/www/routes/_render.ts b/www/routes/_render.ts index 73847be..5f71d18 100644 --- a/www/routes/_render.ts +++ b/www/routes/_render.ts @@ -2,10 +2,14 @@ import { setup } from "../client_deps.ts"; import { RenderContext, RenderFn, virtualSheet } from "../server_deps.ts"; +import * as config from "../tw_config.ts"; const sheet = virtualSheet(); sheet.reset(); -setup({ sheet }); +setup({ + sheet, + ...config +}); export function render(ctx: RenderContext, render: RenderFn) { const snapshot = ctx.state.get("twindSnapshot") as unknown[] | null; diff --git a/www/routes/blog/[...slug].tsx b/www/routes/blog/[...slug].tsx index 6fc1b87..f7f5e20 100644 --- a/www/routes/blog/[...slug].tsx +++ b/www/routes/blog/[...slug].tsx @@ -1,9 +1,10 @@ /** @jsx h */ /** @jsxFrag Fragment */ -import { apply, Fragment, h, Head, PageProps, tw } from "../../client_deps.ts"; +import { Fragment, h, Head, PageProps, tw } from "../../client_deps.ts"; import { gfm, Handlers } from "../../server_deps.ts"; import Footer from "../../components/Footer.tsx"; +import Header from "../../components/Header.tsx"; import NavigationBar from "../../components/NavigationBar.tsx"; import { BlogPost, POSTS } from "../../data/blog.ts"; @@ -42,35 +43,15 @@ export default function BlogPage(props: PageProps) { return ( <> - Whistle blog + {props.data.page.title} - Whistle Blog -
    - -
    -