diff --git a/.gitignore b/.gitignore index 1b2ee77..23de0f3 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,8 @@ /node_modules -/pnpm-lock.yaml \ No newline at end of file +/pnpm-lock.yaml +src/surrealql.grammar.* +/dist +/test/*.js +/test/*.d.ts +/test/*.d.ts.map +.tern-* \ No newline at end of file diff --git a/.npmignore b/.npmignore new file mode 100644 index 0000000..1519e8d --- /dev/null +++ b/.npmignore @@ -0,0 +1,6 @@ +/src +/test +/node_modules +.tern-* +rollup.config.js +tsconfig.json \ No newline at end of file diff --git a/package.json b/package.json index 1997093..5a1e91b 100644 --- a/package.json +++ b/package.json @@ -1,18 +1,30 @@ { "name": "lang-surrealql", "version": "1.0.0", - "description": "", - "main": "index.js", - "scripts": { - "prepare": "lezer-generator src/surrealql.grammar -o src/surrealql.grammar.js" + "description": "SurrealQL language support for CodeMirror 6", + "main": "dist/index.cjs", + "type": "module", + "exports": { + "import": "./dist/index.js", + "require": "./dist/index.cjs" }, - "keywords": [], - "author": "", - "license": "ISC", + "scripts": { + "test": "cm-runtests", + "prepare": "cm-buildhelper src/surrealql.ts" + }, + "types": "dist/index.d.ts", + "module": "dist/index.js", + "sideEffects": false, + "license": "MIT", "devDependencies": { - "@lezer/generator": "^1.6.0" + "@codemirror/buildhelper": "^1.0.0" }, "dependencies": { + "@codemirror/autocomplete": "^6.0.0", + "@lezer/common": "^1.2.0", + "@lezer/highlight": "^1.0.0", + "@codemirror/language": "^6.0.0", + "@codemirror/state": "^6.0.0", "@lezer/lr": "^1.4.0" } } diff --git a/src/surrealql.grammar b/src/surrealql.grammar index 29bce2f..fa261eb 100644 --- a/src/surrealql.grammar +++ b/src/surrealql.grammar @@ -1,10 +1,162 @@ -@top File { (Identifier | Number | String)+ } +@top Script { + (statement SEMI)* + statement +} -@skip { space } +@top SingleStatement { + statement +} + +@skip { whitespace | LineComment } + +kw { @specialize[@name={term}] } +ckw { @extend[@name={term}] } + +Table { identifier } +RecordID { + Table ":" + (identifier | RIDStart RIDContent RIDEnd | RIDDelim RIDDelimContent RIDDelim | Array | Object) +} +RecordRange { + Table ":" + ( + number? (".." | "..=") number? | + Array? (".." | "..=") Array? | + Object? (".." | "..=") Object? + ) +} +GeneratedRecordID { + Table ":" FunctionCall +} +Variable { + kw<"$"> identifier +} + +StringPrefix { + ckw<"s"> | ckw<"r"> | ckw<"d"> | ckw<"u"> +} + +@skip {} { + String[isolate] { + StringPrefix? + ('"' (stringContentDouble | Escape)* ('"' | "\n") | + "'" (stringContentSingle | Escape)* ("'" | "\n")) + } +} + +@precedence { decimal @left, float @left, integer @left } + +Integer { int } +Decimal { int ("." int)? "dec" } +Float { int (("." int) "f"? | "f" ) } +number { Decimal !decimal | Float !float | Integer !integer } + +Array { + "[" (expression (COMMA expression)*)? "]" +} +Object { + "{" (identifier ":" expression (COMMA identifier ":" expression)*)? "}" +} +Namespace { + identifier +} +FunctionName { + identifier +} +FunctionCall { + (Namespace "::" )? FunctionName "(" (expression (COMMA expression)*)? ")" +} +Constant { + (Namespace "::")? + identifier +} +Cast { + "<" identifier ">" expression +} +Future { + "" "{" + expression + "}" +} +Column { + identifier +} + +ReturnStatement { + kw<"return"> expression +} +BeginStatement { + kw<"begin"> kw<"transaction">? +} +BreakStatement { + kw<"break"> +} +CancelStatement { + kw<"cancel"> kw<"transaction">? +} + +expression[@isGroup=Expression] { + String | + number | + Array | + Object | + FunctionCall | + Future | + Variable | + Cast | + RecordID | + Column +} +statement[@isGroup=Statement] { + ReturnStatement | + BeginStatement | + BreakStatement | + CancelStatement +} + +@local tokens { + blockCommentEnd { "*/" } + blockCommentNewline { "\n" } + @else blockCommentContent +} + +@skip {} { BlockComment { "/*" (blockCommentContent | blockCommentNewline)* blockCommentEnd } } @tokens { - space { @whitespace+ } - Identifier { $[A-Za-z_]+ } - Number { $[0-9]+ } - String { '"' !["]* '"' } -} \ No newline at end of file + whitespace { @whitespace+ } + LineComment { ("//" | "-- ") ![\n]* } + + int { @digit+ } + + identifier { $[a-zA-Z] $[a-zA-Z0-9_]* } + + RIDDelim { "`" } + RIDStart { "⟨" } + RIDEnd { "⟩" } + RIDDelimContent { $[^`]+ } + RIDContent { $[^⟨⟩]+ } + + Divide { "/" } + Multiply { "*" } + Add { "+" } + Subtract { "-" } + LessThan { "<" } + GreaterThan { ">" } + + Escape { + "\\" ("x" hex hex | "u" ("{" hex+ "}" | hex hex hex hex) | ![xu]) + } + hex { @digit | $[a-fA-F] } + stringContentSingle { ![\\\n']+ } + stringContentDouble { ![\\\n"]+ } + + EQ { "=" } + COMMA { "," } + SEMI { ";" } + + "(" ")" "[" "]" "{" "}" + + "." +} + +@detectDelim \ No newline at end of file diff --git a/src/surrealql.ts b/src/surrealql.ts new file mode 100644 index 0000000..3a4c896 --- /dev/null +++ b/src/surrealql.ts @@ -0,0 +1,59 @@ +import { LanguageSupport } from "@codemirror/language" +import {parser as baseParser} from "./surrealql.grammar" +import { LRLanguage } from "@codemirror/language" +import { foldNodeProp } from "@codemirror/language"; +import { indentNodeProp } from "@codemirror/language"; +import { continuedIndent } from "@codemirror/language"; +import { styleTags, tags as t } from "@lezer/highlight"; + +export { baseParser } + +export let parser = baseParser.configure({ + props: [ + foldNodeProp.add({ + BlockComment(tree) { return { from: tree.from + 2, to: tree.to - 2 }} + }), + indentNodeProp.add({ + Statement: continuedIndent() + }), + styleTags({ + Keyword: t.keyword, + Type: t.typeName, + Builtin: t.standard(t.name), + Bits: t.number, + Bytes: t.string, + Bool: t.bool, + Null: t.null, + Number: t.number, + String: t.string, + Identifier: t.name, + QuotedIdentifier: t.special(t.string), + SpecialVar: t.special(t.name), + LineComment: t.lineComment, + BlockComment: t.blockComment, + Operator: t.operator, + "Semi Punctuation": t.punctuation, + "( )": t.paren, + "{ }": t.brace, + "[ ]": t.squareBracket + }) + ] +}); + +const language = LRLanguage.define({ + name: "surrealql", + parser: parser, + languageData: { + commentTokens: {line: "//", block: {open: "/*", close: "*/"}}, + closeBrackets: { + brackets: ["(", "[", "{", "'", '"', "`"] + } + } +}); + +export function surrealql() { + return new LanguageSupport( + language, + [] + ); +}; \ No newline at end of file diff --git a/test.js b/test.js index e69de29..c296640 100644 --- a/test.js +++ b/test.js @@ -0,0 +1,2 @@ +import {baseParser} from "./dist/index.js" +console.log(baseParser.parse('begin transaction').toString()) \ No newline at end of file