Merge pull request #775 from quantified-uncertainty/semantic-highlight
Semantic highlighting
This commit is contained in:
commit
c0b632325e
1
packages/squiggle-lang/.gitignore
vendored
1
packages/squiggle-lang/.gitignore
vendored
|
@ -22,3 +22,4 @@ _coverage
|
|||
coverage
|
||||
.nyc_output/
|
||||
src/rescript/Reducer/Reducer_Peggy/Reducer_Peggy_GeneratedParser.js
|
||||
src/rescript/Reducer/Reducer_Peggy/helpers.js
|
||||
|
|
|
@ -7,7 +7,8 @@
|
|||
"peggy": "peggy --cache",
|
||||
"rescript": "rescript",
|
||||
"build": "yarn build:peggy && yarn build:rescript && yarn build:typescript",
|
||||
"build:peggy": "find . -type f -name *.peggy -exec yarn peggy {} \\;",
|
||||
"build:peggy:helpers": "tsc --module commonjs --outDir src/rescript/Reducer/Reducer_Peggy/ src/rescript/Reducer/Reducer_Peggy/helpers.ts",
|
||||
"build:peggy": "yarn build:peggy:helpers && find . -type f -name *.peggy -exec yarn peggy {} \\;",
|
||||
"build:rescript": "rescript build -with-deps",
|
||||
"build:typescript": "tsc",
|
||||
"bundle": "webpack",
|
||||
|
|
|
@ -10,7 +10,6 @@ import {
|
|||
evaluatePartialUsingExternalBindings,
|
||||
evaluateUsingOptions,
|
||||
foreignFunctionInterface,
|
||||
parse as parseRescript,
|
||||
} from "../rescript/TypescriptInterface.gen";
|
||||
export {
|
||||
makeSampleSetDist,
|
||||
|
@ -32,12 +31,14 @@ import {
|
|||
convertRawToTypescript,
|
||||
lambdaValue,
|
||||
} from "./rescript_interop";
|
||||
import { Ok, result, resultMap, tag, tagged } from "./types";
|
||||
import { result, resultMap, tag, tagged } from "./types";
|
||||
import { Distribution, shape } from "./distribution";
|
||||
|
||||
export { Distribution, resultMap, defaultEnvironment };
|
||||
export type { result, shape, environment, lambdaValue, squiggleExpression };
|
||||
|
||||
export { parse } from "./parse";
|
||||
|
||||
export let defaultSamplingInputs: environment = {
|
||||
sampleCount: 10000,
|
||||
xyPointLength: 10000,
|
||||
|
@ -59,23 +60,6 @@ export function run(
|
|||
return resultMap(res, (x) => createTsExport(x, e));
|
||||
}
|
||||
|
||||
export function parse(
|
||||
squiggleString: string
|
||||
): result<null, Extract<errorValue, { tag: "RESyntaxError" }>> {
|
||||
const maybeExpression = parseRescript(squiggleString);
|
||||
if (maybeExpression.tag === "Ok") {
|
||||
return Ok(null); // TODO - return AST
|
||||
} else {
|
||||
if (
|
||||
typeof maybeExpression.value !== "object" ||
|
||||
maybeExpression.value.tag !== "RESyntaxError"
|
||||
) {
|
||||
throw new Error("Expected syntax error");
|
||||
}
|
||||
return { tag: "Error", value: maybeExpression.value };
|
||||
}
|
||||
}
|
||||
|
||||
// Run Partial. A partial is a block of code that doesn't return a value
|
||||
export function runPartial(
|
||||
squiggleString: string,
|
||||
|
|
23
packages/squiggle-lang/src/js/parse.ts
Normal file
23
packages/squiggle-lang/src/js/parse.ts
Normal file
|
@ -0,0 +1,23 @@
|
|||
import {
|
||||
errorValue,
|
||||
parse as parseRescript,
|
||||
} from "../rescript/TypescriptInterface.gen";
|
||||
import { result } from "./types";
|
||||
import { AnyPeggyNode } from "../rescript/Reducer/Reducer_Peggy/helpers";
|
||||
|
||||
export function parse(
|
||||
squiggleString: string
|
||||
): result<AnyPeggyNode, Extract<errorValue, { tag: "RESyntaxError" }>> {
|
||||
const maybeExpression = parseRescript(squiggleString);
|
||||
if (maybeExpression.tag === "Ok") {
|
||||
return { tag: "Ok", value: maybeExpression.value as AnyPeggyNode };
|
||||
} else {
|
||||
if (
|
||||
typeof maybeExpression.value !== "object" ||
|
||||
maybeExpression.value.tag !== "RESyntaxError"
|
||||
) {
|
||||
throw new Error("Expected syntax error");
|
||||
}
|
||||
return { tag: "Error", value: maybeExpression.value };
|
||||
}
|
||||
}
|
|
@ -1,74 +1,7 @@
|
|||
// Try in https://peggyjs.org/online
|
||||
|
||||
{{
|
||||
var toFunction = {
|
||||
'-': 'subtract',
|
||||
'->': 'pipe',
|
||||
'!=': 'unequal',
|
||||
'.-': 'dotSubtract',
|
||||
'.*': 'dotMultiply',
|
||||
'./': 'dotDivide',
|
||||
'.^': 'dotPow',
|
||||
'.+': 'dotAdd',
|
||||
'*': 'multiply',
|
||||
'/': 'divide',
|
||||
'&&': 'and',
|
||||
'^': 'pow', // or xor
|
||||
'+': 'add',
|
||||
'<': 'smaller',
|
||||
'<=': 'smallerEq',
|
||||
'==': 'equal',
|
||||
'>': 'larger',
|
||||
'>=': 'largerEq',
|
||||
'||': 'or',
|
||||
'to': 'credibleIntervalToDistribution',
|
||||
}
|
||||
|
||||
var unaryToFunction = {
|
||||
'-': 'unaryMinus',
|
||||
'!': 'not',
|
||||
'.-': 'unaryDotMinus',
|
||||
}
|
||||
|
||||
var postOperatorToFunction = {
|
||||
'.': '$_atIndex_$',
|
||||
'()': '$$_applyAll_$$',
|
||||
'[]': '$_atIndex_$',
|
||||
}
|
||||
|
||||
function makeFunctionCall(fn, args) {
|
||||
if (fn === '$$_applyAll_$$') {
|
||||
// Any list of values is applied from left to right anyway.
|
||||
// Like in Haskell and Lisp.
|
||||
// So we remove the redundant $$_applyAll_$$.
|
||||
if (args[0].type === "Identifier") {args[0].type = "CallIdentifier"}
|
||||
return nodeExpression(args)
|
||||
} else {
|
||||
return nodeExpression([nodeCallIndentifier(fn), ...args])
|
||||
}
|
||||
}
|
||||
|
||||
function apply(fn, arg) { return makeFunctionCall(fn, [arg]); }
|
||||
function constructArray(elems) { return apply('$_constructArray_$', nodeExpression(elems)); }
|
||||
function constructRecord(elems) { return apply('$_constructRecord_$', nodeExpression(elems)); }
|
||||
|
||||
function nodeBlock(statements) {return{type: 'Block', statements: statements}}
|
||||
function nodeBoolean(value) {return {type: 'Boolean', value: value}}
|
||||
function nodeCallIndentifier(value) {return {type: 'CallIdentifier', value: value}}
|
||||
function nodeExpression(args) {return {type: 'Expression', nodes: args}}
|
||||
function nodeFloat(value) {return {type: 'Float', value: value}}
|
||||
function nodeIdentifier(value) {return {type: 'Identifier', value: value}}
|
||||
function nodeInteger(value) {return {type: 'Integer', value: value}}
|
||||
function nodeKeyValue(key, value) {
|
||||
if (key.type === 'Identifier') {key.type = 'String'}
|
||||
return {type: 'KeyValue', key: key, value: value}}
|
||||
function nodeLambda(args, body) {return {type: 'Lambda', args: args, body: body}}
|
||||
function nodeLetStatment(variable, value) {return {type: 'LetStatement', variable: variable, value: value}}
|
||||
function nodeModuleIdentifier(value) {return {type: 'ModuleIdentifier', value: value}}
|
||||
function nodeString(value) {return {type: 'String', value: value}}
|
||||
function nodeTernary(condition, trueExpression, falseExpression) {return {type: 'Ternary', condition: condition, trueExpression: trueExpression, falseExpression: falseExpression}}
|
||||
|
||||
function nodeTypeIdentifier(typeValue) {return {type: 'TypeIdentifier', value: typeValue}}
|
||||
const h = require('./helpers');
|
||||
}}
|
||||
|
||||
start
|
||||
|
@ -80,21 +13,21 @@ zeroOMoreArgumentsBlockOrExpression = innerBlockOrExpression / lambda
|
|||
outerBlock
|
||||
= statements:array_statements finalExpression: (statementSeparator @expression)?
|
||||
{ if (finalExpression != null) { statements.push(finalExpression) }
|
||||
return nodeBlock(statements) }
|
||||
return h.nodeBlock(statements) }
|
||||
/ finalExpression: expression
|
||||
{ return nodeBlock([finalExpression])}
|
||||
{ return h.nodeBlock([finalExpression])}
|
||||
|
||||
innerBlockOrExpression
|
||||
= quotedInnerBlock
|
||||
/ finalExpression: expression
|
||||
{ return nodeBlock([finalExpression])}
|
||||
{ return h.nodeBlock([finalExpression])}
|
||||
|
||||
quotedInnerBlock
|
||||
= '{' _nl statements:array_statements finalExpression: (statementSeparator @expression) _nl '}'
|
||||
{ statements.push(finalExpression)
|
||||
return nodeBlock(statements) }
|
||||
return h.nodeBlock(statements) }
|
||||
/ '{' _nl finalExpression: expression _nl '}'
|
||||
{ return nodeBlock([finalExpression]) }
|
||||
{ return h.nodeBlock([finalExpression]) }
|
||||
|
||||
array_statements
|
||||
= head:statement tail:(statementSeparator @array_statements )
|
||||
|
@ -109,12 +42,12 @@ statement
|
|||
|
||||
letStatement
|
||||
= variable:identifier _ assignmentOp _nl value:zeroOMoreArgumentsBlockOrExpression
|
||||
{ return nodeLetStatment(variable, value) }
|
||||
{ return h.nodeLetStatement(variable, value) }
|
||||
|
||||
defunStatement
|
||||
= variable:identifier '(' _nl args:array_parameters _nl ')' _ assignmentOp _nl body:innerBlockOrExpression
|
||||
{ var value = nodeLambda(args, body)
|
||||
return nodeLetStatment(variable, value) }
|
||||
{ var value = h.nodeLambda(args, body)
|
||||
return h.nodeLetStatement(variable, value) }
|
||||
|
||||
assignmentOp "assignment" = '='
|
||||
|
||||
|
@ -128,16 +61,16 @@ ifthenelse
|
|||
= 'if' __nl condition:logicalAdditive
|
||||
__nl 'then' __nl trueExpression:innerBlockOrExpression
|
||||
__nl 'else' __nl falseExpression:(ifthenelse/innerBlockOrExpression)
|
||||
{ return nodeTernary(condition, trueExpression, falseExpression) }
|
||||
{ return h.nodeTernary(condition, trueExpression, falseExpression) }
|
||||
|
||||
ternary
|
||||
= condition:logicalAdditive _ '?' _nl trueExpression:logicalAdditive _ ':' _nl falseExpression:(ternary/logicalAdditive)
|
||||
{ return nodeTernary(condition, trueExpression, falseExpression) }
|
||||
{ return h.nodeTernary(condition, trueExpression, falseExpression) }
|
||||
|
||||
logicalAdditive
|
||||
= head:logicalMultiplicative tail:(_ operator:logicalAdditiveOp _nl arg:logicalMultiplicative {return {operator: operator, right: arg}})*
|
||||
{ return tail.reduce(function(result, element) {
|
||||
return makeFunctionCall(toFunction[element.operator], [result, element.right])
|
||||
return h.makeFunctionCall(h.toFunction[element.operator], [result, element.right])
|
||||
}, head)}
|
||||
|
||||
logicalAdditiveOp "operator" = '||'
|
||||
|
@ -146,21 +79,21 @@ logicalAdditive
|
|||
logicalMultiplicative
|
||||
= head:equality tail:(_ operator:logicalMultiplicativeOp _nl arg:equality {return {operator: operator, right: arg}})*
|
||||
{ return tail.reduce(function(result, element) {
|
||||
return makeFunctionCall(toFunction[element.operator], [result, element.right])
|
||||
return h.makeFunctionCall(h.toFunction[element.operator], [result, element.right])
|
||||
}, head)}
|
||||
|
||||
logicalMultiplicativeOp "operator" = '&&'
|
||||
|
||||
equality
|
||||
= left:relational _ operator:equalityOp _nl right:relational
|
||||
{ return makeFunctionCall(toFunction[operator], [left, right])}
|
||||
{ return h.makeFunctionCall(h.toFunction[operator], [left, right])}
|
||||
/ relational
|
||||
|
||||
equalityOp "operator" = '=='/'!='
|
||||
|
||||
relational
|
||||
= left:additive _ operator:relationalOp _nl right:additive
|
||||
{ return makeFunctionCall(toFunction[operator], [left, right])}
|
||||
{ return h.makeFunctionCall(h.toFunction[operator], [left, right])}
|
||||
/ additive
|
||||
|
||||
relationalOp "operator" = '<='/'<'/'>='/'>'
|
||||
|
@ -168,7 +101,7 @@ relational
|
|||
additive
|
||||
= head:multiplicative tail:(_ operator:additiveOp _nl arg:multiplicative {return {operator: operator, right: arg}})*
|
||||
{ return tail.reduce(function(result, element) {
|
||||
return makeFunctionCall(toFunction[element.operator], [result, element.right])
|
||||
return h.makeFunctionCall(h.toFunction[element.operator], [result, element.right])
|
||||
}, head)}
|
||||
|
||||
additiveOp "operator" = '+' / '-' / '.+' / '.-'
|
||||
|
@ -176,7 +109,7 @@ additive
|
|||
multiplicative
|
||||
= head:power tail:(_ operator:multiplicativeOp _nl arg:power {return {operator: operator, right: arg}})*
|
||||
{ return tail.reduce(function(result, element) {
|
||||
return makeFunctionCall(toFunction[element.operator], [result, element.right])
|
||||
return h.makeFunctionCall(h.toFunction[element.operator], [result, element.right])
|
||||
}, head)}
|
||||
|
||||
multiplicativeOp "operator" = '*' / '/' / '.*' / './'
|
||||
|
@ -184,7 +117,7 @@ multiplicative
|
|||
power
|
||||
= head:credibleInterval tail:(_ operator:powerOp _nl arg:credibleInterval {return {operator: operator, right: arg}})*
|
||||
{ return tail.reduce(function(result, element) {
|
||||
return makeFunctionCall(toFunction[element.operator], [result, element.right])
|
||||
return h.makeFunctionCall(h.toFunction[element.operator], [result, element.right])
|
||||
}, head)}
|
||||
|
||||
powerOp "operator" = '^' / '.^'
|
||||
|
@ -192,7 +125,7 @@ power
|
|||
credibleInterval
|
||||
= head:chainFunctionCall tail:(__ operator:credibleIntervalOp __nl arg:chainFunctionCall {return {operator: operator, right: arg}})*
|
||||
{ return tail.reduce(function(result, element) {
|
||||
return makeFunctionCall(toFunction[element.operator], [result, element.right])
|
||||
return h.makeFunctionCall(h.toFunction[element.operator], [result, element.right])
|
||||
}, head)}
|
||||
|
||||
credibleIntervalOp "operator" = 'to'
|
||||
|
@ -200,7 +133,7 @@ credibleInterval
|
|||
chainFunctionCall
|
||||
= head:unary tail:(_ ('->'/'|>') _nl chained:chainedFunction {return chained})*
|
||||
{ return tail.reduce(function(result, element) {
|
||||
return makeFunctionCall(element.fnName, [result, ...element.args])
|
||||
return h.makeFunctionCall(element.fnName, [result, ...element.args])
|
||||
}, head)}
|
||||
|
||||
chainedFunction
|
||||
|
@ -215,7 +148,7 @@ chainFunctionCall
|
|||
|
||||
unary
|
||||
= unaryOperator:unaryOperator _nl right:(unary/postOperator)
|
||||
{ return apply(unaryToFunction[unaryOperator], right)}
|
||||
{ return h.apply(h.unaryToFunction[unaryOperator], right)}
|
||||
/ postOperator
|
||||
|
||||
unaryOperator "unary operator"
|
||||
|
@ -230,12 +163,12 @@ indexedValue
|
|||
collectionElement
|
||||
= head:atom &('['/'('/'.')
|
||||
tail:(
|
||||
_ '[' _nl arg:expression _nl ']' {return {fn: postOperatorToFunction['[]'], args: [arg]}}
|
||||
/ _ '(' _nl args:array_functionArguments _nl ')' {return {fn: postOperatorToFunction['()'], args: args}}
|
||||
/ '.' arg:$dollarIdentifier {return {fn: postOperatorToFunction['[]'], args: [nodeString(arg)]}}
|
||||
_ '[' _nl arg:expression _nl ']' {return {fn: h.postOperatorToFunction['[]'], args: [arg]}}
|
||||
/ _ '(' _nl args:array_functionArguments _nl ')' {return {fn: h.postOperatorToFunction['()'], args: args}}
|
||||
/ '.' arg:$dollarIdentifier {return {fn: h.postOperatorToFunction['[]'], args: [h.nodeString(arg)]}}
|
||||
)*
|
||||
{ return tail.reduce(function(result, element) {
|
||||
return makeFunctionCall(element.fn, [result, ...element.args])
|
||||
return h.makeFunctionCall(element.fn, [result, ...element.args])
|
||||
}, head)}
|
||||
|
||||
array_functionArguments
|
||||
|
@ -261,49 +194,49 @@ dollarIdentifierWithModule 'identifier'
|
|||
final:$dollarIdentifier
|
||||
{ tail.push(final);
|
||||
return tail.reduce(function(result, element) {
|
||||
return makeFunctionCall(postOperatorToFunction['[]'], [result, nodeString(element)])
|
||||
return h.makeFunctionCall(h.postOperatorToFunction['[]'], [result, h.nodeString(element)])
|
||||
}, head)}
|
||||
|
||||
identifier 'identifier'
|
||||
= ([_a-z]+[_a-z0-9]i*) {return nodeIdentifier(text())}
|
||||
= ([_a-z]+[_a-z0-9]i*) {return h.nodeIdentifier(text(), location())}
|
||||
|
||||
unitIdentifier 'identifier'
|
||||
= ([_a-zA-Z]+[_a-z0-9]i*) {return nodeIdentifier(text())}
|
||||
= ([_a-zA-Z]+[_a-z0-9]i*) {return h.nodeIdentifier(text(), location())}
|
||||
|
||||
dollarIdentifier '$identifier'
|
||||
= ([\$_a-z]+[\$_a-z0-9]i*) {return nodeIdentifier(text())}
|
||||
= ([\$_a-z]+[\$_a-z0-9]i*) {return h.nodeIdentifier(text(), location())}
|
||||
|
||||
moduleIdentifier 'identifier'
|
||||
= ([A-Z]+[_a-z0-9]i*) {return nodeModuleIdentifier(text())}
|
||||
= ([A-Z]+[_a-z0-9]i*) {return h.nodeModuleIdentifier(text())}
|
||||
|
||||
|
||||
string 'string'
|
||||
= characters:("'" @([^'])* "'") {return nodeString(characters.join(''))}
|
||||
/ characters:('"' @([^"])* '"') {return nodeString(characters.join(''))}
|
||||
= characters:("'" @([^'])* "'") {return h.nodeString(characters.join(''))}
|
||||
/ characters:('"' @([^"])* '"') {return h.nodeString(characters.join(''))}
|
||||
|
||||
number = number:(float / integer) unit:unitIdentifier?
|
||||
{
|
||||
if (unit === null)
|
||||
{ return number }
|
||||
else
|
||||
{ return apply('fromUnit_'+unit.value, number)
|
||||
{ return h.apply('fromUnit_'+unit.value, number)
|
||||
}
|
||||
}
|
||||
|
||||
integer 'integer'
|
||||
= d+ !"\." ![e]i
|
||||
{ return nodeInteger(parseInt(text()))}
|
||||
{ return h.nodeInteger(parseInt(text()))}
|
||||
|
||||
float 'float'
|
||||
= $(((d+ "\." d*) / ("\." d+)) floatExponent? / d+ floatExponent)
|
||||
{ return nodeFloat(parseFloat(text()))}
|
||||
{ return h.nodeFloat(parseFloat(text()))}
|
||||
|
||||
floatExponent = [e]i '-'? d+
|
||||
d = [0-9]
|
||||
|
||||
boolean 'boolean'
|
||||
= ('true'/'false')
|
||||
{ return nodeBoolean(text() === 'true')}
|
||||
{ return h.nodeBoolean(text() === 'true')}
|
||||
|
||||
valueConstructor
|
||||
= recordConstructor
|
||||
|
@ -314,15 +247,15 @@ valueConstructor
|
|||
lambda
|
||||
= '{' _nl '|' _nl args:array_parameters _nl '|' _nl statements:array_statements finalExpression: (statementSeparator @expression) _nl '}'
|
||||
{ statements.push(finalExpression)
|
||||
return nodeLambda(args, nodeBlock(statements)) }
|
||||
return h.nodeLambda(args, h.nodeBlock(statements)) }
|
||||
/ '{' _nl '|' _nl args:array_parameters _nl '|' _nl finalExpression: expression _nl '}'
|
||||
{ return nodeLambda(args, nodeBlock([finalExpression])) }
|
||||
{ return h.nodeLambda(args, h.nodeBlock([finalExpression])) }
|
||||
|
||||
arrayConstructor 'array'
|
||||
= '[' _nl ']'
|
||||
{ return constructArray([]); }
|
||||
{ return h.constructArray([]); }
|
||||
/ '[' _nl args:array_elements _nl ']'
|
||||
{ return constructArray(args); }
|
||||
{ return h.constructArray(args); }
|
||||
|
||||
array_elements
|
||||
= head:expression tail:(_ ',' _nl @expression)*
|
||||
|
@ -330,7 +263,7 @@ arrayConstructor 'array'
|
|||
|
||||
recordConstructor 'record'
|
||||
= '{' _nl args:array_recordArguments _nl '}'
|
||||
{ return constructRecord(args); }
|
||||
{ return h.constructRecord(args); }
|
||||
|
||||
array_recordArguments
|
||||
= head:keyValuePair tail:(_ ',' _nl @keyValuePair)*
|
||||
|
@ -338,7 +271,7 @@ recordConstructor 'record'
|
|||
|
||||
keyValuePair
|
||||
= key:expression _ ':' _nl value:expression
|
||||
{ return nodeKeyValue(key, value)}
|
||||
{ return h.nodeKeyValue(key, value)}
|
||||
|
||||
// Separators
|
||||
|
||||
|
@ -377,30 +310,30 @@ statementSeparator 'statement separator'
|
|||
noArguments = ('(' _nl ')' )?
|
||||
|
||||
typeIdentifier 'type identifier'
|
||||
= ([a-z]+[_a-z0-9]i*) {return nodeTypeIdentifier(text())}
|
||||
= ([a-z]+[_a-z0-9]i*) {return h.nodeTypeIdentifier(text())}
|
||||
|
||||
typeConstructorIdentifier 'type constructor identifier'
|
||||
= ([A-Z]+[_a-z0-9]i*) {return nodeTypeIdentifier(text())}
|
||||
= ([A-Z]+[_a-z0-9]i*) {return h.nodeTypeIdentifier(text())}
|
||||
|
||||
typeExpression = typePostModifierExpression
|
||||
|
||||
typePostModifierExpression = head:typeOr tail:(_ '$' _nl @typeModifier)*
|
||||
{
|
||||
return tail.reduce((result, element) => {
|
||||
return makeFunctionCall('$_typeModifier_'+element.modifier.value+'_$', [result, ...element.args])
|
||||
return h.makeFunctionCall('$_typeModifier_'+element.modifier.value+'_$', [result, ...element.args])
|
||||
}, head)
|
||||
}
|
||||
|
||||
typeOr = head:typeFunction tail:(_ '|' _nl @typeFunction)*
|
||||
{ return tail.length === 0 ? head : apply('$_typeOr_$', constructArray([head, ...tail])); }
|
||||
{ return tail.length === 0 ? head : h.apply('$_typeOr_$', h.constructArray([head, ...tail])); }
|
||||
|
||||
typeFunction = head:typeModifierExpression tail:(_ '=>' _nl @typeModifierExpression)*
|
||||
{ return tail.length === 0 ? head : apply( '$_typeFunction_$', constructArray([head, ...tail])); }
|
||||
{ return tail.length === 0 ? head : h.apply( '$_typeFunction_$', h.constructArray([head, ...tail])); }
|
||||
|
||||
typeModifierExpression = head:basicType tail:(_ '<-' _nl @typeModifier)*
|
||||
{
|
||||
return tail.reduce((result, element) => {
|
||||
return makeFunctionCall('$_typeModifier_'+element.modifier.value+'_$', [result, ...element.args])
|
||||
return h.makeFunctionCall('$_typeModifier_'+element.modifier.value+'_$', [result, ...element.args])
|
||||
}, head)
|
||||
}
|
||||
|
||||
|
@ -413,10 +346,10 @@ typeModifierExpression = head:basicType tail:(_ '<-' _nl @typeModifier)*
|
|||
basicType = typeConstructor / typeArray / typeRecord / typeInParanthesis / typeIdentifier
|
||||
|
||||
typeArray = '[' _nl elem:typeExpression _nl ']'
|
||||
{return apply('$_typeArray_$', elem)}
|
||||
{return h.apply('$_typeArray_$', elem)}
|
||||
|
||||
typeRecord = '{' _nl elems:array_typeRecordArguments _nl '}'
|
||||
{ return apply('$_typeRecord_$', constructRecord(elems)); }
|
||||
{ return h.apply('$_typeRecord_$', h.constructRecord(elems)); }
|
||||
|
||||
array_typeRecordArguments
|
||||
= head:typeKeyValuePair tail:(_ ',' _nl @typeKeyValuePair)*
|
||||
|
@ -424,22 +357,22 @@ typeRecord = '{' _nl elems:array_typeRecordArguments _nl '}'
|
|||
|
||||
typeKeyValuePair
|
||||
= key:identifier _ ':' _nl value:typeExpression
|
||||
{ return nodeKeyValue(key, value)}
|
||||
{ return h.nodeKeyValue(key, value)}
|
||||
|
||||
typeConstructor
|
||||
= constructor:typeConstructorIdentifier _ '(' _nl args:array_types _nl ')'
|
||||
{ return makeFunctionCall('$_typeConstructor_$', [constructor, constructArray(args)]); }
|
||||
{ return h.makeFunctionCall('$_typeConstructor_$', [constructor, h.constructArray(args)]); }
|
||||
/ constructor:typeConstructorIdentifier _ noArguments
|
||||
{ return makeFunctionCall('$_typeConstructor_$', [constructor, constructArray([])]); }
|
||||
{ return h.makeFunctionCall('$_typeConstructor_$', [constructor, h.constructArray([])]); }
|
||||
|
||||
array_types = head:typeExpression tail:(_ ',' _nl @typeExpression)*
|
||||
{ return [head, ...tail]; }
|
||||
|
||||
typeStatement = typeAliasStatement / typeOfStatement
|
||||
typeAliasStatement = 'type' __nl typeIdentifier:typeIdentifier _nl '=' _nl typeExpression:typeExpression
|
||||
{ return makeFunctionCall('$_typeAlias_$', [typeIdentifier, typeExpression])}
|
||||
{ return h.makeFunctionCall('$_typeAlias_$', [typeIdentifier, typeExpression])}
|
||||
typeOfStatement = identifier:identifier _ ':' _nl typeExpression:typeExpression
|
||||
{ return makeFunctionCall('$_typeOf_$', [identifier, typeExpression])}
|
||||
{ return h.makeFunctionCall('$_typeOf_$', [identifier, typeExpression])}
|
||||
|
||||
typeInParanthesis = '(' _nl typeExpression:typeExpression _nl ')' {return typeExpression}
|
||||
|
||||
|
|
|
@ -0,0 +1,215 @@
|
|||
import { LocationRange } from "peggy";
|
||||
|
||||
export const toFunction = {
|
||||
"-": "subtract",
|
||||
"->": "pipe",
|
||||
"!=": "unequal",
|
||||
".-": "dotSubtract",
|
||||
".*": "dotMultiply",
|
||||
"./": "dotDivide",
|
||||
".^": "dotPow",
|
||||
".+": "dotAdd",
|
||||
"*": "multiply",
|
||||
"/": "divide",
|
||||
"&&": "and",
|
||||
"^": "pow", // or xor
|
||||
"+": "add",
|
||||
"<": "smaller",
|
||||
"<=": "smallerEq",
|
||||
"==": "equal",
|
||||
">": "larger",
|
||||
">=": "largerEq",
|
||||
"||": "or",
|
||||
to: "credibleIntervalToDistribution",
|
||||
};
|
||||
|
||||
export const unaryToFunction = {
|
||||
"-": "unaryMinus",
|
||||
"!": "not",
|
||||
".-": "unaryDotMinus",
|
||||
};
|
||||
|
||||
export const postOperatorToFunction = {
|
||||
".": "$_atIndex_$",
|
||||
"()": "$$_applyAll_$$",
|
||||
"[]": "$_atIndex_$",
|
||||
};
|
||||
|
||||
type NodeBlock = {
|
||||
type: "Block";
|
||||
statements: AnyPeggyNode[];
|
||||
};
|
||||
|
||||
type NodeExpression = {
|
||||
type: "Expression";
|
||||
nodes: AnyPeggyNode[];
|
||||
};
|
||||
|
||||
type NodeFloat = {
|
||||
type: "Float";
|
||||
value: number;
|
||||
};
|
||||
|
||||
type NodeInteger = {
|
||||
type: "Integer";
|
||||
value: number;
|
||||
};
|
||||
|
||||
type NodeIdentifier = {
|
||||
type: "Identifier";
|
||||
value: string;
|
||||
location: LocationRange;
|
||||
};
|
||||
|
||||
type NodeCallIdentifier = {
|
||||
type: "CallIdentifier";
|
||||
value: string;
|
||||
};
|
||||
|
||||
type NodeLetStatement = {
|
||||
type: "LetStatement";
|
||||
variable: NodeIdentifier;
|
||||
value: AnyPeggyNode;
|
||||
};
|
||||
|
||||
type NodeLambda = {
|
||||
type: "Lambda";
|
||||
args: AnyPeggyNode[];
|
||||
body: AnyPeggyNode;
|
||||
};
|
||||
|
||||
type NodeTernary = {
|
||||
type: "Ternary";
|
||||
condition: AnyPeggyNode;
|
||||
trueExpression: AnyPeggyNode;
|
||||
falseExpression: AnyPeggyNode;
|
||||
};
|
||||
|
||||
type NodeKeyValue = {
|
||||
type: "KeyValue";
|
||||
key: AnyPeggyNode;
|
||||
value: AnyPeggyNode;
|
||||
};
|
||||
|
||||
type NodeString = {
|
||||
type: "String";
|
||||
value: string;
|
||||
location?: LocationRange;
|
||||
};
|
||||
|
||||
type NodeBoolean = {
|
||||
type: "Boolean";
|
||||
value: boolean;
|
||||
};
|
||||
|
||||
export type AnyPeggyNode =
|
||||
| NodeBlock
|
||||
| NodeExpression
|
||||
| NodeFloat
|
||||
| NodeInteger
|
||||
| NodeIdentifier
|
||||
| NodeCallIdentifier
|
||||
| NodeLetStatement
|
||||
| NodeLambda
|
||||
| NodeTernary
|
||||
| NodeKeyValue
|
||||
| NodeString
|
||||
| NodeBoolean;
|
||||
|
||||
export function makeFunctionCall(fn: string, args: AnyPeggyNode[]) {
|
||||
if (fn === "$$_applyAll_$$") {
|
||||
// Any list of values is applied from left to right anyway.
|
||||
// Like in Haskell and Lisp.
|
||||
// So we remove the redundant $$_applyAll_$$.
|
||||
if (args[0].type === "Identifier") {
|
||||
args[0] = {
|
||||
...args[0],
|
||||
type: "CallIdentifier",
|
||||
};
|
||||
}
|
||||
return nodeExpression(args);
|
||||
} else {
|
||||
return nodeExpression([nodeCallIndentifier(fn), ...args]);
|
||||
}
|
||||
}
|
||||
|
||||
export function apply(fn: string, arg: AnyPeggyNode) {
|
||||
return makeFunctionCall(fn, [arg]);
|
||||
}
|
||||
export function constructArray(elems: AnyPeggyNode[]) {
|
||||
return apply("$_constructArray_$", nodeExpression(elems));
|
||||
}
|
||||
export function constructRecord(elems: AnyPeggyNode[]) {
|
||||
return apply("$_constructRecord_$", nodeExpression(elems));
|
||||
}
|
||||
|
||||
export function nodeBlock(statements: AnyPeggyNode[]): NodeBlock {
|
||||
return { type: "Block", statements };
|
||||
}
|
||||
export function nodeBoolean(value: boolean): NodeBoolean {
|
||||
return { type: "Boolean", value };
|
||||
}
|
||||
export function nodeCallIndentifier(value: string): NodeCallIdentifier {
|
||||
return { type: "CallIdentifier", value };
|
||||
}
|
||||
export function nodeExpression(args: AnyPeggyNode[]): NodeExpression {
|
||||
return { type: "Expression", nodes: args };
|
||||
}
|
||||
export function nodeFloat(value: number): NodeFloat {
|
||||
return { type: "Float", value };
|
||||
}
|
||||
export function nodeIdentifier(
|
||||
value: string,
|
||||
location: LocationRange
|
||||
): NodeIdentifier {
|
||||
return { type: "Identifier", value, location };
|
||||
}
|
||||
export function nodeInteger(value: number): NodeInteger {
|
||||
return { type: "Integer", value };
|
||||
}
|
||||
export function nodeKeyValue(
|
||||
key: AnyPeggyNode,
|
||||
value: AnyPeggyNode
|
||||
): NodeKeyValue {
|
||||
if (key.type === "Identifier") {
|
||||
key = {
|
||||
...key,
|
||||
type: "String",
|
||||
};
|
||||
}
|
||||
return { type: "KeyValue", key, value };
|
||||
}
|
||||
export function nodeLambda(
|
||||
args: AnyPeggyNode[],
|
||||
body: AnyPeggyNode
|
||||
): NodeLambda {
|
||||
return { type: "Lambda", args, body };
|
||||
}
|
||||
export function nodeLetStatement(
|
||||
variable: NodeIdentifier,
|
||||
value: AnyPeggyNode
|
||||
): NodeLetStatement {
|
||||
return { type: "LetStatement", variable, value };
|
||||
}
|
||||
export function nodeModuleIdentifier(value: string) {
|
||||
return { type: "ModuleIdentifier", value };
|
||||
}
|
||||
export function nodeString(value: string): NodeString {
|
||||
return { type: "String", value };
|
||||
}
|
||||
export function nodeTernary(
|
||||
condition: AnyPeggyNode,
|
||||
trueExpression: AnyPeggyNode,
|
||||
falseExpression: AnyPeggyNode
|
||||
): NodeTernary {
|
||||
return {
|
||||
type: "Ternary",
|
||||
condition,
|
||||
trueExpression,
|
||||
falseExpression,
|
||||
};
|
||||
}
|
||||
|
||||
export function nodeTypeIdentifier(typeValue: string) {
|
||||
return { type: "TypeIdentifier", value: typeValue };
|
||||
}
|
|
@ -4,6 +4,7 @@ import * as vscode from "vscode";
|
|||
import { startClient, stopClient } from "./client";
|
||||
|
||||
import { SquiggleEditorProvider } from "./editor";
|
||||
import { registerSemanticHighlight } from "./highlight";
|
||||
import { registerPreviewCommand } from "./preview";
|
||||
|
||||
// this method is called when your extension is activated
|
||||
|
@ -13,6 +14,8 @@ export function activate(context: vscode.ExtensionContext) {
|
|||
|
||||
registerPreviewCommand(context);
|
||||
|
||||
registerSemanticHighlight();
|
||||
|
||||
startClient(context);
|
||||
}
|
||||
|
||||
|
|
96
packages/vscode-ext/client/src/highlight.ts
Normal file
96
packages/vscode-ext/client/src/highlight.ts
Normal file
|
@ -0,0 +1,96 @@
|
|||
import * as vscode from "vscode";
|
||||
|
||||
import { parse } from "@quri/squiggle-lang";
|
||||
import { AnyPeggyNode } from "@quri/squiggle-lang/dist/src/rescript/Reducer/Reducer_Peggy/helpers";
|
||||
|
||||
const tokenTypes = ["enum", "function", "variable", "property"];
|
||||
const tokenModifiers = ["declaration", "documentation"];
|
||||
const legend = new vscode.SemanticTokensLegend(tokenTypes, tokenModifiers);
|
||||
|
||||
const convertRange = (
|
||||
range: Extract<AnyPeggyNode, { type: "Identifier" }>["location"]
|
||||
) =>
|
||||
new vscode.Range(
|
||||
new vscode.Position(range.start.line - 1, range.start.column - 1),
|
||||
new vscode.Position(range.end.line - 1, range.end.column - 1)
|
||||
);
|
||||
|
||||
const populateTokensBuilder = (
|
||||
tokensBuilder: vscode.SemanticTokensBuilder,
|
||||
node: AnyPeggyNode
|
||||
// bindings: { [key: string]: boolean }
|
||||
) => {
|
||||
switch (node.type) {
|
||||
case "Expression":
|
||||
for (const child of node.nodes) {
|
||||
populateTokensBuilder(tokensBuilder, child);
|
||||
}
|
||||
break;
|
||||
case "Block":
|
||||
for (const child of node.statements) {
|
||||
populateTokensBuilder(tokensBuilder, child);
|
||||
}
|
||||
break;
|
||||
case "LetStatement":
|
||||
tokensBuilder.push(
|
||||
convertRange(node.variable.location),
|
||||
node.value.type === "Lambda" ? "function" : "variable",
|
||||
["declaration"]
|
||||
);
|
||||
populateTokensBuilder(tokensBuilder, node.value);
|
||||
break;
|
||||
case "Lambda":
|
||||
for (const arg of node.args) {
|
||||
populateTokensBuilder(tokensBuilder, arg);
|
||||
}
|
||||
populateTokensBuilder(tokensBuilder, node.body);
|
||||
break;
|
||||
case "Ternary":
|
||||
populateTokensBuilder(tokensBuilder, node.condition);
|
||||
populateTokensBuilder(tokensBuilder, node.trueExpression);
|
||||
populateTokensBuilder(tokensBuilder, node.falseExpression);
|
||||
break;
|
||||
case "KeyValue":
|
||||
if (node.key.type === "String" && node.key.location) {
|
||||
tokensBuilder.push(convertRange(node.key.location), "property", [
|
||||
"declaration",
|
||||
]);
|
||||
} else {
|
||||
populateTokensBuilder(tokensBuilder, node.key);
|
||||
}
|
||||
populateTokensBuilder(tokensBuilder, node.value);
|
||||
break;
|
||||
case "Identifier":
|
||||
tokensBuilder.push(convertRange(node.location), "variable");
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
export const registerSemanticHighlight = () => {
|
||||
const provider: vscode.DocumentSemanticTokensProvider = {
|
||||
provideDocumentSemanticTokens(
|
||||
document: vscode.TextDocument
|
||||
): vscode.ProviderResult<vscode.SemanticTokens> {
|
||||
const parseResult = parse(document.getText());
|
||||
|
||||
const tokensBuilder = new vscode.SemanticTokensBuilder(legend);
|
||||
if (parseResult.tag === "Ok") {
|
||||
populateTokensBuilder(
|
||||
tokensBuilder,
|
||||
parseResult.value
|
||||
// {}
|
||||
);
|
||||
}
|
||||
|
||||
return tokensBuilder.build();
|
||||
},
|
||||
};
|
||||
|
||||
const selector = { language: "squiggle", scheme: "file" };
|
||||
|
||||
vscode.languages.registerDocumentSemanticTokensProvider(
|
||||
selector,
|
||||
provider,
|
||||
legend
|
||||
);
|
||||
};
|
|
@ -13,6 +13,10 @@ import { parse } from "@quri/squiggle-lang";
|
|||
|
||||
import { TextDocument } from "vscode-languageserver-textdocument";
|
||||
|
||||
// Documentation:
|
||||
// - https://code.visualstudio.com/api/language-extensions/language-server-extension-guide
|
||||
// - https://microsoft.github.io/language-server-protocol/specifications/specification-current
|
||||
|
||||
// Create a connection for the server, using Node's IPC as a transport.
|
||||
// Also include all preview / proposed LSP features.
|
||||
let connection = createConnection(ProposedFeatures.all);
|
||||
|
@ -23,17 +27,7 @@ documents.onDidChangeContent((change) => {
|
|||
validateSquiggleDocument(change.document);
|
||||
});
|
||||
|
||||
let hasDiagnosticRelatedInformationCapability = false;
|
||||
|
||||
connection.onInitialize((params: InitializeParams) => {
|
||||
const capabilities = params.capabilities;
|
||||
|
||||
hasDiagnosticRelatedInformationCapability = !!(
|
||||
capabilities.textDocument &&
|
||||
capabilities.textDocument.publishDiagnostics &&
|
||||
capabilities.textDocument.publishDiagnostics.relatedInformation
|
||||
);
|
||||
|
||||
const result: InitializeResult = {
|
||||
capabilities: {
|
||||
textDocumentSync: TextDocumentSyncKind.Incremental,
|
||||
|
|
Loading…
Reference in New Issue
Block a user