Compare commits
4 commits
16efe31b5f
...
7f548796f2
Author | SHA1 | Date | |
---|---|---|---|
7f548796f2 | |||
15b87fc15d | |||
77ae54fa18 | |||
5da8dcefe5 |
22 changed files with 388 additions and 52 deletions
|
@ -74,6 +74,7 @@ Repeater {
|
||||||
icon: `settings/custom/${propertyIcon}.svg`
|
icon: `settings/custom/${propertyIcon}.svg`
|
||||||
defValue: Utils.simplifyExpression(obj[propertyName].toEditableString())
|
defValue: Utils.simplifyExpression(obj[propertyName].toEditableString())
|
||||||
self: obj.name
|
self: obj.name
|
||||||
|
variables: propertyType.variables
|
||||||
onChanged: function(newExpr) {
|
onChanged: function(newExpr) {
|
||||||
if(obj[propertyName].toString() != newExpr.toString()) {
|
if(obj[propertyName].toString() != newExpr.toString()) {
|
||||||
history.addToHistory(new HistoryLib.EditedProperty(
|
history.addToHistory(new HistoryLib.EditedProperty(
|
||||||
|
@ -230,8 +231,8 @@ Repeater {
|
||||||
dictionaryMode: paramTypeIn(propertyType, ['Dict'])
|
dictionaryMode: paramTypeIn(propertyType, ['Dict'])
|
||||||
keyType: dictionaryMode ? propertyType.keyType : 'string'
|
keyType: dictionaryMode ? propertyType.keyType : 'string'
|
||||||
valueType: propertyType.valueType
|
valueType: propertyType.valueType
|
||||||
preKeyLabel: (dictionaryMode ? propertyType.preKeyLabel : propertyType.label).replace(/\{name\}/g, obj.name)
|
preKeyLabel: (dictionaryMode ? propertyType.preKeyLabel : propertyType.label).replace(/\{name\}/g, obj.name).replace(/\{name_\}/g, obj.name.substring(obj.name.indexOf("_")+1))
|
||||||
postKeyLabel: (dictionaryMode ? propertyType.postKeyLabel : '').replace(/\{name\}/g, obj.name)
|
postKeyLabel: (dictionaryMode ? propertyType.postKeyLabel : '').replace(/\{name\}/g, obj.name).replace(/\{name_\}/g, obj.name.substring(obj.name.indexOf("_")+1))
|
||||||
keyRegexp: dictionaryMode ? propertyType.keyFormat : /^.+$/
|
keyRegexp: dictionaryMode ? propertyType.keyFormat : /^.+$/
|
||||||
valueRegexp: propertyType.format
|
valueRegexp: propertyType.format
|
||||||
forbidAdding: propertyType.forbidAdding
|
forbidAdding: propertyType.forbidAdding
|
||||||
|
@ -267,7 +268,7 @@ Repeater {
|
||||||
return commentComponent
|
return commentComponent
|
||||||
else if(propertyType == 'boolean')
|
else if(propertyType == 'boolean')
|
||||||
return checkboxComponent
|
return checkboxComponent
|
||||||
else if(propertyType == 'Expression')
|
else if(paramTypeIn(propertyType, ['Expression']))
|
||||||
return expressionEditorComponent
|
return expressionEditorComponent
|
||||||
else if(paramTypeIn(propertyType, textTypes))
|
else if(paramTypeIn(propertyType, textTypes))
|
||||||
return textEditorComponent
|
return textEditorComponent
|
||||||
|
|
|
@ -58,6 +58,9 @@ D.Dialog {
|
||||||
width: 350
|
width: 350
|
||||||
height: 400
|
height: 400
|
||||||
|
|
||||||
|
// Disable closing on return/enter, causing issues with autocomplete.
|
||||||
|
onActionChosen: if(action.key == Qt.Key_Enter || action.key == Qt.Key_Return) action.accepted = false
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
id: dlgTitle
|
id: dlgTitle
|
||||||
anchors.left: parent.left
|
anchors.left: parent.left
|
||||||
|
|
|
@ -49,7 +49,7 @@ ScrollView {
|
||||||
id: objectsListView
|
id: objectsListView
|
||||||
model: Object.keys(Objects.types)
|
model: Object.keys(Objects.types)
|
||||||
//width: implicitWidth //objectListList.width - (implicitHeight > objectListList.parent.height ? 20 : 0)
|
//width: implicitWidth //objectListList.width - (implicitHeight > objectListList.parent.height ? 20 : 0)
|
||||||
implicitHeight: contentItem.childrenRect.height + footer.height + 10
|
implicitHeight: contentItem.childrenRect.height + footerItem.height + 10
|
||||||
|
|
||||||
delegate: ListView {
|
delegate: ListView {
|
||||||
id: objTypeList
|
id: objTypeList
|
||||||
|
|
|
@ -0,0 +1,115 @@
|
||||||
|
/**
|
||||||
|
* LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
|
||||||
|
* Copyright (C) 2022 Ad5001
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import QtQuick 2.12
|
||||||
|
import QtQuick.Controls 2.12
|
||||||
|
|
||||||
|
/*!
|
||||||
|
\qmltype AutocompletionCategory
|
||||||
|
\inqmlmodule eu.ad5001.LogarithmPlotter.Setting
|
||||||
|
\brief ListView representing a category of autocompletion.
|
||||||
|
|
||||||
|
\sa ExpressionEditor
|
||||||
|
*/
|
||||||
|
ListView {
|
||||||
|
id: listFiltered
|
||||||
|
/*!
|
||||||
|
\qmlproperty int AutocompletionCategory::itemStartIndex
|
||||||
|
Start index of the first element in this list compared to the global autocompletion index.
|
||||||
|
*/
|
||||||
|
property int itemStartIndex: 0
|
||||||
|
/*!
|
||||||
|
\qmlproperty int AutocompletionCategory::itemSelected
|
||||||
|
The global autocompletion index.
|
||||||
|
*/
|
||||||
|
property int itemSelected: 0
|
||||||
|
|
||||||
|
/*!
|
||||||
|
\qmlproperty string AutocompletionCategory::category
|
||||||
|
Name of the category.
|
||||||
|
*/
|
||||||
|
property string category: ""
|
||||||
|
|
||||||
|
/*!
|
||||||
|
\qmlproperty var AutocompletionCategory::categoryItems
|
||||||
|
List of items in this category. To be filtered by the autocomplete to filters.
|
||||||
|
*/
|
||||||
|
property var categoryItems: []
|
||||||
|
|
||||||
|
/*!
|
||||||
|
\qmlproperty var AutocompletionCategory::autocompleteGenerator
|
||||||
|
Javascript function taking the name of the item to create an autocompletion item (dictionary with
|
||||||
|
a 'text', 'annotation' 'autocomplete', and 'cursorFinalOffset' keys.
|
||||||
|
*/
|
||||||
|
property var autocompleteGenerator: (item) => {return {'text': item, 'autocomplete': item, 'annotation': '', 'cursorFinalOffset': 0}}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
\qmlproperty string AutocompletionCategory::baseText
|
||||||
|
Text to autocomplete.
|
||||||
|
*/
|
||||||
|
property string baseText: ""
|
||||||
|
width: parent.width
|
||||||
|
visible: model.length > 0
|
||||||
|
implicitHeight: contentItem.childrenRect.height
|
||||||
|
model: parent.visible ? categoryItems.filter((item) => item.includes(baseText)).map(autocompleteGenerator) : []
|
||||||
|
|
||||||
|
header: Column {
|
||||||
|
width: listFiltered.width
|
||||||
|
spacing: 2
|
||||||
|
topPadding: 5
|
||||||
|
bottomPadding: 5
|
||||||
|
|
||||||
|
Text {
|
||||||
|
leftPadding: 5
|
||||||
|
text: listFiltered.category
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
height: 1
|
||||||
|
color: 'black'
|
||||||
|
width: parent.width
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
delegate: Rectangle {
|
||||||
|
property bool selected: index + listFiltered.itemStartIndex == listFiltered.itemSelected
|
||||||
|
|
||||||
|
width: listFiltered.width
|
||||||
|
height: Math.max(autocompleteText.height, annotationText.height)
|
||||||
|
color: selected ? sysPalette.highlight : 'transparent'
|
||||||
|
|
||||||
|
Text {
|
||||||
|
id: autocompleteText
|
||||||
|
topPadding: 2
|
||||||
|
bottomPadding: 2
|
||||||
|
leftPadding: 15
|
||||||
|
text: listFiltered.model[index].text
|
||||||
|
color: parent.selected ? sysPalette.highlightedText : sysPalette.windowText
|
||||||
|
}
|
||||||
|
|
||||||
|
Text {
|
||||||
|
id: annotationText
|
||||||
|
anchors.right: parent.right
|
||||||
|
topPadding: 2
|
||||||
|
bottomPadding: 2
|
||||||
|
rightPadding: 15
|
||||||
|
text: listFiltered.model[index].annotation
|
||||||
|
color: parent.selected ? sysPaletteIn.highlightedText : sysPaletteIn.windowText
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -19,9 +19,10 @@
|
||||||
import QtQuick.Controls 2.12
|
import QtQuick.Controls 2.12
|
||||||
import QtQuick 2.12
|
import QtQuick 2.12
|
||||||
import QtQuick.Dialogs 1.3 as D
|
import QtQuick.Dialogs 1.3 as D
|
||||||
import eu.ad5001.LogarithmPlotter.Popup 1.0 as Popup
|
import eu.ad5001.LogarithmPlotter.Popup 1.0 as P
|
||||||
import "../js/mathlib.js" as MathLib
|
import "../js/mathlib.js" as MathLib
|
||||||
import "../js/utils.js" as Utils
|
import "../js/utils.js" as Utils
|
||||||
|
import "../js/objects.js" as Objects
|
||||||
import "../js/parsing/parsing.js" as Parsing
|
import "../js/parsing/parsing.js" as Parsing
|
||||||
|
|
||||||
|
|
||||||
|
@ -30,7 +31,7 @@ import "../js/parsing/parsing.js" as Parsing
|
||||||
\inqmlmodule eu.ad5001.LogarithmPlotter.Setting
|
\inqmlmodule eu.ad5001.LogarithmPlotter.Setting
|
||||||
\brief Setting to edit strings and numbers.
|
\brief Setting to edit strings and numbers.
|
||||||
|
|
||||||
\sa EditorDialog, Settings, Icon
|
\sa EditorDialog, AutocompletionCategory
|
||||||
*/
|
*/
|
||||||
Item {
|
Item {
|
||||||
id: control
|
id: control
|
||||||
|
@ -60,6 +61,11 @@ Item {
|
||||||
Used to prevent circular dependency.
|
Used to prevent circular dependency.
|
||||||
*/
|
*/
|
||||||
property string self: ""
|
property string self: ""
|
||||||
|
/*!
|
||||||
|
\qmlproperty var ExpressionEditor::variables
|
||||||
|
Accepted variables for the expression.
|
||||||
|
*/
|
||||||
|
property var variables: []
|
||||||
/*!
|
/*!
|
||||||
\qmlproperty string ExpressionEditor::placeholderText
|
\qmlproperty string ExpressionEditor::placeholderText
|
||||||
Value of the editor.
|
Value of the editor.
|
||||||
|
@ -151,6 +157,8 @@ Item {
|
||||||
focus: true
|
focus: true
|
||||||
selectByMouse: true
|
selectByMouse: true
|
||||||
|
|
||||||
|
property var tokens: parent.tokens(text)
|
||||||
|
|
||||||
Keys.priority: Keys.BeforeItem // Required for knowing which key the user presses.
|
Keys.priority: Keys.BeforeItem // Required for knowing which key the user presses.
|
||||||
|
|
||||||
onEditingFinished: {
|
onEditingFinished: {
|
||||||
|
@ -165,7 +173,44 @@ Item {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//onTextEdited: acPopupContent.itemSelected = 0
|
||||||
|
|
||||||
|
onActiveFocusChanged: {
|
||||||
|
if(activeFocus)
|
||||||
|
autocompletePopup.open()
|
||||||
|
else
|
||||||
|
autocompletePopup.close()
|
||||||
|
}
|
||||||
|
|
||||||
|
Keys.onUpPressed: function(event) {
|
||||||
|
if(acPopupContent.itemSelected == 0)
|
||||||
|
acPopupContent.itemSelected = acPopupContent.itemCount-1
|
||||||
|
else
|
||||||
|
acPopupContent.itemSelected = acPopupContent.itemSelected-1
|
||||||
|
event.accepted = true
|
||||||
|
}
|
||||||
|
|
||||||
|
Keys.onDownPressed: function(event) {
|
||||||
|
if(acPopupContent.itemSelected == Math.min(acPopupContent.itemCount-1))
|
||||||
|
acPopupContent.itemSelected = 0
|
||||||
|
else
|
||||||
|
acPopupContent.itemSelected = acPopupContent.itemSelected+1
|
||||||
|
event.accepted = true
|
||||||
|
}
|
||||||
|
|
||||||
Keys.onPressed: function(event) {
|
Keys.onPressed: function(event) {
|
||||||
|
// Autocomplete popup events
|
||||||
|
//console.log("Pressed key:", event.key, Qt.Key_Return, Qt.Key_Enter, event.text)
|
||||||
|
if((event.key == Qt.Key_Enter || event.key == Qt.Key_Return) && acPopupContent.itemCount > 0) {
|
||||||
|
acPopupContent.autocomplete()
|
||||||
|
event.accepted = true
|
||||||
|
} else
|
||||||
|
acPopupContent.itemSelected = 0
|
||||||
|
/*if(event.key == Qt.Key_Left) { // TODO: Don't reset the position when the key moved is still on the same word
|
||||||
|
if(!acPopupContent.identifierTokenTypes.includes())
|
||||||
|
}*/
|
||||||
|
|
||||||
|
|
||||||
if(event.text in openAndCloseMatches) {
|
if(event.text in openAndCloseMatches) {
|
||||||
let start = selectionStart
|
let start = selectionStart
|
||||||
insert(selectionStart, event.text)
|
insert(selectionStart, event.text)
|
||||||
|
@ -181,11 +226,137 @@ Item {
|
||||||
verticalAlignment: TextInput.AlignVCenter
|
verticalAlignment: TextInput.AlignVCenter
|
||||||
horizontalAlignment: control.label == "" ? TextInput.AlignLeft : TextInput.AlignHCenter
|
horizontalAlignment: control.label == "" ? TextInput.AlignLeft : TextInput.AlignHCenter
|
||||||
textFormat: Text.StyledText
|
textFormat: Text.StyledText
|
||||||
text: colorize(editor.text)
|
text: colorize(parent.tokens)
|
||||||
color: sysPalette.windowText
|
color: sysPalette.windowText
|
||||||
//font.pixelSize: parent.font.pixelSize
|
//font.pixelSize: parent.font.pixelSize
|
||||||
//opacity: editor.activeFocus ? 0 : 1
|
//opacity: editor.activeFocus ? 0 : 1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Popup {
|
||||||
|
id: autocompletePopup
|
||||||
|
x: 0
|
||||||
|
y: parent.height
|
||||||
|
closePolicy: Popup.NoAutoClose
|
||||||
|
|
||||||
|
width: editor.width
|
||||||
|
height: acPopupContent.height
|
||||||
|
padding: 0
|
||||||
|
|
||||||
|
Column {
|
||||||
|
id: acPopupContent
|
||||||
|
width: parent.width
|
||||||
|
|
||||||
|
readonly property var identifierTokenTypes: [
|
||||||
|
Parsing.TokenType.VARIABLE,
|
||||||
|
Parsing.TokenType.FUNCTION,
|
||||||
|
Parsing.TokenType.CONSTANT
|
||||||
|
]
|
||||||
|
property var currentToken: getTokenAt(editor.tokens, editor.cursorPosition)
|
||||||
|
visible: currentToken != null && identifierTokenTypes.includes(currentToken.type)
|
||||||
|
|
||||||
|
// Focus handling.
|
||||||
|
readonly property var lists: [variablesList, constantsList, functionsList, executableObjectsList, objectsList]
|
||||||
|
readonly property int itemCount: variablesList.model.length, constantsList.model.length + functionsList.model.length + executableObjectsList.model.length + objectsList.model.length
|
||||||
|
property int itemSelected: 0
|
||||||
|
|
||||||
|
/*!
|
||||||
|
\qmlmethod var ExpressionEditor::autocompleteAt(int idx)
|
||||||
|
Returns the autocompletion text information at a given position.
|
||||||
|
The information contains key 'text' (description text), 'autocomplete' (text to insert)
|
||||||
|
and 'cursorFinalOffset' (amount to add to the cursor's position after the end of the autocomplete)
|
||||||
|
*/
|
||||||
|
function autocompleteAt(idx) {
|
||||||
|
if(idx >= itemCount) return ""
|
||||||
|
let startIndex = 0
|
||||||
|
for(let list of lists) {
|
||||||
|
if(idx < startIndex + list.model.length)
|
||||||
|
return list.model[idx-startIndex]
|
||||||
|
startIndex += list.model.length
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/*!
|
||||||
|
\qmlmethod var ExpressionEditor::autocomplete()
|
||||||
|
Autocompletes with the current selected word.
|
||||||
|
*/
|
||||||
|
function autocomplete() {
|
||||||
|
let autotext = autocompleteAt(itemSelected)
|
||||||
|
let startPos = currentToken.startPosition
|
||||||
|
editor.remove(startPos, startPos+currentToken.value.length)
|
||||||
|
editor.insert(startPos, autotext.autocomplete)
|
||||||
|
editor.cursorPosition = startPos+autotext.autocomplete.length+autotext.cursorFinalOffset
|
||||||
|
}
|
||||||
|
|
||||||
|
AutocompletionCategory {
|
||||||
|
id: variablesList
|
||||||
|
|
||||||
|
category: qsTr("Variables")
|
||||||
|
itemStartIndex: 0
|
||||||
|
itemSelected: parent.itemSelected
|
||||||
|
categoryItems: control.variables
|
||||||
|
autocompleteGenerator: (item) => {return {
|
||||||
|
'text': item, 'annotation': '',
|
||||||
|
'autocomplete': item + " ", 'cursorFinalOffset': 0
|
||||||
|
}}
|
||||||
|
baseText: parent.visible ? parent.currentToken.value : ""
|
||||||
|
}
|
||||||
|
|
||||||
|
AutocompletionCategory {
|
||||||
|
id: constantsList
|
||||||
|
|
||||||
|
category: qsTr("Constants")
|
||||||
|
itemStartIndex: variablesList.model.length
|
||||||
|
itemSelected: parent.itemSelected
|
||||||
|
categoryItems: Parsing.CONSTANTS_LIST
|
||||||
|
autocompleteGenerator: (item) => {return {
|
||||||
|
'text': item, 'annotation': '',
|
||||||
|
'autocomplete': item + " ", 'cursorFinalOffset': 0
|
||||||
|
}}
|
||||||
|
baseText: parent.visible ? parent.currentToken.value : ""
|
||||||
|
}
|
||||||
|
|
||||||
|
AutocompletionCategory {
|
||||||
|
id: functionsList
|
||||||
|
|
||||||
|
category: qsTr("Functions")
|
||||||
|
itemStartIndex: variablesList.model.length + constantsList.model.length
|
||||||
|
itemSelected: parent.itemSelected
|
||||||
|
categoryItems: Parsing.FUNCTIONS_LIST
|
||||||
|
autocompleteGenerator: (item) => {return {
|
||||||
|
'text': item, 'annotation': '',
|
||||||
|
'autocomplete': item+'()', 'cursorFinalOffset': -1
|
||||||
|
}}
|
||||||
|
baseText: parent.visible ? parent.currentToken.value : ""
|
||||||
|
}
|
||||||
|
|
||||||
|
AutocompletionCategory {
|
||||||
|
id: executableObjectsList
|
||||||
|
|
||||||
|
category: qsTr("Executable Objects")
|
||||||
|
itemStartIndex: variablesList.model.length + constantsList.model.length + functionsList.model.length
|
||||||
|
itemSelected: parent.itemSelected
|
||||||
|
categoryItems: Objects.getObjectsName("ExecutableObject").filter(obj => obj != self)
|
||||||
|
autocompleteGenerator: (item) => {return {
|
||||||
|
'text': item, 'annotation': `${Objects.currentObjectsByName[item].constructor.displayType()}`,
|
||||||
|
'autocomplete': item+'()', 'cursorFinalOffset': -1
|
||||||
|
}}
|
||||||
|
baseText: parent.visible ? parent.currentToken.value : ""
|
||||||
|
}
|
||||||
|
|
||||||
|
AutocompletionCategory {
|
||||||
|
id: objectsList
|
||||||
|
|
||||||
|
category: qsTr("Objects")
|
||||||
|
itemStartIndex: executableObjectsList.model.length + variablesList.model.length + constantsList.model.length + functionsList.model.length
|
||||||
|
itemSelected: parent.itemSelected
|
||||||
|
categoryItems: Object.keys(Objects.currentObjectsByName).filter(obj => obj != self)
|
||||||
|
autocompleteGenerator: (item) => {return {
|
||||||
|
'text': item, 'annotation': `${Objects.currentObjectsByName[item].constructor.displayType()}`,
|
||||||
|
'autocomplete': item+'.', 'cursorFinalOffset': 0
|
||||||
|
}}
|
||||||
|
baseText: parent.visible ? parent.currentToken.value : ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Button {
|
Button {
|
||||||
|
@ -202,7 +373,7 @@ Item {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Popup.InsertCharacter {
|
P.InsertCharacter {
|
||||||
id: insertPopup
|
id: insertPopup
|
||||||
|
|
||||||
x: Math.round((parent.width - width) / 2)
|
x: Math.round((parent.width - width) / 2)
|
||||||
|
@ -228,7 +399,6 @@ Item {
|
||||||
// Check if the expression is valid, throws error otherwise.
|
// Check if the expression is valid, throws error otherwise.
|
||||||
if(!expr.allRequirementsFullfilled()) {
|
if(!expr.allRequirementsFullfilled()) {
|
||||||
let undefVars = expr.undefinedVariables()
|
let undefVars = expr.undefinedVariables()
|
||||||
console.log(JSON.stringify(undefVars), undefVars.join(', '))
|
|
||||||
if(undefVars.length > 1)
|
if(undefVars.length > 1)
|
||||||
throw new Error(qsTranslate('error', 'No object found with names %1.').arg(undefVars.join(', ')))
|
throw new Error(qsTranslate('error', 'No object found with names %1.').arg(undefVars.join(', ')))
|
||||||
else
|
else
|
||||||
|
@ -245,15 +415,41 @@ Item {
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
\qmlmethod var ExpressionEditor::colorize(string expressionText)
|
\qmlmethod var ExpressionEditor::tokens(string expressionText)
|
||||||
Creates an HTML colorized string of the incomplete \c expressionText.
|
Generates a list of tokens from the given.
|
||||||
|
*/
|
||||||
|
function tokens(text) {
|
||||||
|
let tokenizer = new Parsing.Tokenizer(new Parsing.Input(text), true, false)
|
||||||
|
let tokenList = []
|
||||||
|
let token
|
||||||
|
while((token = tokenizer.next()) != null)
|
||||||
|
tokenList.push(token)
|
||||||
|
return tokenList
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
\qmlmethod var ExpressionEditor::getTokenAt(var tokens, int position)
|
||||||
|
Gets the token at the given position within the text.
|
||||||
|
Returns null if out of bounds.
|
||||||
|
*/
|
||||||
|
function getTokenAt(tokenList, position) {
|
||||||
|
let currentPosition = 0
|
||||||
|
for(let token of tokenList)
|
||||||
|
if(position <= (currentPosition + token.value.length))
|
||||||
|
return token
|
||||||
|
else
|
||||||
|
currentPosition += token.value.length
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
\qmlmethod var ExpressionEditor::colorize(var tokenList)
|
||||||
|
Creates an HTML colorized string of the given tokens.
|
||||||
Returns the colorized and escaped expression if possible, null otherwise..
|
Returns the colorized and escaped expression if possible, null otherwise..
|
||||||
*/
|
*/
|
||||||
function colorize(text) {
|
function colorize(tokenList) {
|
||||||
let tokenizer = new Parsing.Tokenizer(new Parsing.Input(text), true, false)
|
|
||||||
let parsedText = ""
|
let parsedText = ""
|
||||||
let token
|
for(let token of tokenList) {
|
||||||
while((token = tokenizer.next()) != null) {
|
|
||||||
switch(token.type) {
|
switch(token.type) {
|
||||||
case Parsing.TokenType.VARIABLE:
|
case Parsing.TokenType.VARIABLE:
|
||||||
parsedText += `<font color="${colorScheme.VARIABLE}">${token.value}</font>`
|
parsedText += `<font color="${colorScheme.VARIABLE}">${token.value}</font>`
|
||||||
|
|
|
@ -5,3 +5,4 @@ Icon 1.0 Icon.qml
|
||||||
ListSetting 1.0 ListSetting.qml
|
ListSetting 1.0 ListSetting.qml
|
||||||
TextSetting 1.0 TextSetting.qml
|
TextSetting 1.0 TextSetting.qml
|
||||||
ExpressionEditor 1.0 ExpressionEditor.qml
|
ExpressionEditor 1.0 ExpressionEditor.qml
|
||||||
|
AutocompletionCategory 1.0 AutocompletionCategory.qml
|
||||||
|
|
|
@ -75,7 +75,7 @@ class EditedPosition extends C.Action {
|
||||||
}
|
}
|
||||||
|
|
||||||
export() {
|
export() {
|
||||||
return [this.targetName, this.targetType, this.targetProperty,
|
return [this.targetName, this.targetType,
|
||||||
this.previousXValue.toEditableString(), this.newXValue.toEditableString(),
|
this.previousXValue.toEditableString(), this.newXValue.toEditableString(),
|
||||||
this.previousYValue.toEditableString(), this.newYValue.toEditableString()]
|
this.previousYValue.toEditableString(), this.newYValue.toEditableString()]
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,6 +28,7 @@ var evalVariables = { // Variables not provided by expr-eval.js, needs to be pro
|
||||||
"pi": Math.PI,
|
"pi": Math.PI,
|
||||||
"π": Math.PI,
|
"π": Math.PI,
|
||||||
"inf": Infinity,
|
"inf": Infinity,
|
||||||
|
"infinity": Infinity,
|
||||||
"Infinity": Infinity,
|
"Infinity": Infinity,
|
||||||
"∞": Infinity,
|
"∞": Infinity,
|
||||||
"e": Math.E,
|
"e": Math.E,
|
||||||
|
|
|
@ -148,9 +148,11 @@ function expression(tokens) {
|
||||||
|
|
||||||
switch(type) {
|
switch(type) {
|
||||||
case ExprEval.INUMBER:
|
case ExprEval.INUMBER:
|
||||||
if (typeof item.value === 'number' && item.value < 0) {
|
if(item.value == Infinity) {
|
||||||
|
nstack.push("\\infty")
|
||||||
|
} else if(typeof item.value === 'number' && item.value < 0) {
|
||||||
nstack.push(par(item.value));
|
nstack.push(par(item.value));
|
||||||
} else if (Array.isArray(item.value)) {
|
} else if(Array.isArray(item.value)) {
|
||||||
nstack.push('[' + item.value.map(ExprEval.escapeValue).join(', ') + ']');
|
nstack.push('[' + item.value.map(ExprEval.escapeValue).join(', ') + ']');
|
||||||
} else {
|
} else {
|
||||||
nstack.push(ExprEval.escapeValue(item.value));
|
nstack.push(ExprEval.escapeValue(item.value));
|
||||||
|
|
|
@ -58,11 +58,12 @@ function getObjectsName(objType) {
|
||||||
* @return {array} List of names of the objects.
|
* @return {array} List of names of the objects.
|
||||||
*/
|
*/
|
||||||
if(objType == "ExecutableObject") {
|
if(objType == "ExecutableObject") {
|
||||||
var types = getExecutableTypes()
|
// NOTE: QMLJS does not support flatMap.
|
||||||
var elementNames = ['']
|
// return getExecutableTypes().flatMap(elemType => currentObjects[elemType].map(obj => obj.name))
|
||||||
types.forEach(function(elemType){
|
let types = getExecutableTypes()
|
||||||
|
let elementNames = ['']
|
||||||
|
for(let elemType of types)
|
||||||
elementNames = elementNames.concat(currentObjects[elemType].map(obj => obj.name))
|
elementNames = elementNames.concat(currentObjects[elemType].map(obj => obj.name))
|
||||||
})
|
|
||||||
return elementNames
|
return elementNames
|
||||||
}
|
}
|
||||||
if(currentObjects[objType] == undefined) return []
|
if(currentObjects[objType] == undefined) return []
|
||||||
|
|
|
@ -30,9 +30,10 @@
|
||||||
* Creates a new name for an object, based on the \c allowedLetters.
|
* Creates a new name for an object, based on the \c allowedLetters.
|
||||||
* If variables with each of the allowedLetters is created, a subscript
|
* If variables with each of the allowedLetters is created, a subscript
|
||||||
* number is added to the name.
|
* number is added to the name.
|
||||||
|
* @param {string} prefix - Prefix to the name.
|
||||||
* @return {string} New unused name for a new object.
|
* @return {string} New unused name for a new object.
|
||||||
*/
|
*/
|
||||||
function getNewName(allowedLetters) {
|
function getNewName(allowedLetters, prefix='') {
|
||||||
// Allows to get a new name, based on the allowed letters,
|
// Allows to get a new name, based on the allowed letters,
|
||||||
// as well as adding a sub number when needs be.
|
// as well as adding a sub number when needs be.
|
||||||
var newid = 0
|
var newid = 0
|
||||||
|
@ -40,7 +41,7 @@ function getNewName(allowedLetters) {
|
||||||
do {
|
do {
|
||||||
var letter = allowedLetters[newid % allowedLetters.length]
|
var letter = allowedLetters[newid % allowedLetters.length]
|
||||||
var num = Math.floor((newid - (newid % allowedLetters.length)) / allowedLetters.length)
|
var num = Math.floor((newid - (newid % allowedLetters.length)) / allowedLetters.length)
|
||||||
ret = letter + (num > 0 ? Utils.textsub(num-1) : '')
|
ret = prefix + letter + (num > 0 ? Utils.textsub(num-1) : '')
|
||||||
newid += 1
|
newid += 1
|
||||||
} while(ret in Objects.currentObjectsByName)
|
} while(ret in Objects.currentObjectsByName)
|
||||||
return ret
|
return ret
|
||||||
|
@ -80,10 +81,10 @@ class DrawableObject {
|
||||||
* List of properties used in the Object Editor.
|
* List of properties used in the Object Editor.
|
||||||
*
|
*
|
||||||
* Properties are set with key as property name and
|
* Properties are set with key as property name and
|
||||||
* value as it's type name (e.g 'Expression', 'string'...),
|
* value as it's type name (e.g 'numbers', 'string'...),
|
||||||
* an Enum for enumerations, an ObjectType for DrawableObjects
|
* an Enum for enumerations, an ObjectType for DrawableObjects
|
||||||
* with a specific type, a List instance for lists, a
|
* with a specific type, a List instance for lists, a
|
||||||
* Dictionary instance for dictionaries...
|
* Dictionary instance for dictionaries, an Expression for expressions...
|
||||||
*
|
*
|
||||||
* If the property is to be translated, the key should be passed
|
* If the property is to be translated, the key should be passed
|
||||||
* through the QT_TRANSLATE_NOOP macro in that form:
|
* through the QT_TRANSLATE_NOOP macro in that form:
|
||||||
|
|
|
@ -30,7 +30,7 @@ class Function extends Common.ExecutableObject {
|
||||||
static displayType(){return qsTr('Function')}
|
static displayType(){return qsTr('Function')}
|
||||||
static displayTypeMultiple(){return qsTr('Functions')}
|
static displayTypeMultiple(){return qsTr('Functions')}
|
||||||
static properties() {return {
|
static properties() {return {
|
||||||
[QT_TRANSLATE_NOOP('prop','expression')]: 'Expression',
|
[QT_TRANSLATE_NOOP('prop','expression')]: new P.Expression('x'),
|
||||||
[QT_TRANSLATE_NOOP('prop','definitionDomain')]: 'Domain',
|
[QT_TRANSLATE_NOOP('prop','definitionDomain')]: 'Domain',
|
||||||
[QT_TRANSLATE_NOOP('prop','destinationDomain')]: 'Domain',
|
[QT_TRANSLATE_NOOP('prop','destinationDomain')]: 'Domain',
|
||||||
'comment1': QT_TRANSLATE_NOOP(
|
'comment1': QT_TRANSLATE_NOOP(
|
||||||
|
|
|
@ -35,7 +35,7 @@ class GainBode extends Common.ExecutableObject {
|
||||||
static properties() {return {
|
static properties() {return {
|
||||||
[QT_TRANSLATE_NOOP('prop','om_0')]: new P.ObjectType('Point'),
|
[QT_TRANSLATE_NOOP('prop','om_0')]: new P.ObjectType('Point'),
|
||||||
[QT_TRANSLATE_NOOP('prop','pass')]: P.Enum.BodePass,
|
[QT_TRANSLATE_NOOP('prop','pass')]: P.Enum.BodePass,
|
||||||
[QT_TRANSLATE_NOOP('prop','gain')]: 'Expression',
|
[QT_TRANSLATE_NOOP('prop','gain')]: new P.Expression(),
|
||||||
[QT_TRANSLATE_NOOP('prop','labelPosition')]: P.Enum.Position,
|
[QT_TRANSLATE_NOOP('prop','labelPosition')]: P.Enum.Position,
|
||||||
[QT_TRANSLATE_NOOP('prop','labelX')]: 'number',
|
[QT_TRANSLATE_NOOP('prop','labelX')]: 'number',
|
||||||
[QT_TRANSLATE_NOOP('prop','omGraduation')]: 'boolean'
|
[QT_TRANSLATE_NOOP('prop','omGraduation')]: 'boolean'
|
||||||
|
|
|
@ -32,7 +32,7 @@ class PhaseBode extends Common.ExecutableObject {
|
||||||
static displayTypeMultiple(){return qsTr('Bode Phases')}
|
static displayTypeMultiple(){return qsTr('Bode Phases')}
|
||||||
static properties() {return {
|
static properties() {return {
|
||||||
[QT_TRANSLATE_NOOP('prop','om_0')]: new P.ObjectType('Point'),
|
[QT_TRANSLATE_NOOP('prop','om_0')]: new P.ObjectType('Point'),
|
||||||
[QT_TRANSLATE_NOOP('prop','phase')]: 'Expression',
|
[QT_TRANSLATE_NOOP('prop','phase')]: new P.Expression(),
|
||||||
[QT_TRANSLATE_NOOP('prop','unit')]: new P.Enum('°', 'deg', 'rad'),
|
[QT_TRANSLATE_NOOP('prop','unit')]: new P.Enum('°', 'deg', 'rad'),
|
||||||
[QT_TRANSLATE_NOOP('prop','labelPosition')]: P.Enum.Position,
|
[QT_TRANSLATE_NOOP('prop','labelPosition')]: P.Enum.Position,
|
||||||
[QT_TRANSLATE_NOOP('prop','labelX')]: 'number'
|
[QT_TRANSLATE_NOOP('prop','labelX')]: 'number'
|
||||||
|
|
|
@ -30,8 +30,8 @@ class Point extends Common.DrawableObject {
|
||||||
static displayTypeMultiple(){return qsTr('Points')}
|
static displayTypeMultiple(){return qsTr('Points')}
|
||||||
|
|
||||||
static properties() {return {
|
static properties() {return {
|
||||||
[QT_TRANSLATE_NOOP('prop','x')]: 'Expression',
|
[QT_TRANSLATE_NOOP('prop','x')]: new P.Expression(),
|
||||||
[QT_TRANSLATE_NOOP('prop','y')]: 'Expression',
|
[QT_TRANSLATE_NOOP('prop','y')]: new P.Expression(),
|
||||||
[QT_TRANSLATE_NOOP('prop','labelPosition')]: P.Enum.Position,
|
[QT_TRANSLATE_NOOP('prop','labelPosition')]: P.Enum.Position,
|
||||||
[QT_TRANSLATE_NOOP('prop','pointStyle')]: new P.Enum('●', '✕', '+')
|
[QT_TRANSLATE_NOOP('prop','pointStyle')]: new P.Enum('●', '✕', '+')
|
||||||
}}
|
}}
|
||||||
|
|
|
@ -34,12 +34,12 @@ class RepartitionFunction extends Common.ExecutableObject {
|
||||||
'comment',
|
'comment',
|
||||||
'Note: Specify the probability for each value.'
|
'Note: Specify the probability for each value.'
|
||||||
),
|
),
|
||||||
[QT_TRANSLATE_NOOP('prop','probabilities')]: new P.Dictionary('string', 'float', /^-?[\d.,]+$/, /^-?[\d\.,]+$/, 'P({name} = ', ') = '),
|
[QT_TRANSLATE_NOOP('prop','probabilities')]: new P.Dictionary('string', 'float', /^-?[\d.,]+$/, /^-?[\d\.,]+$/, 'P({name_} = ', ') = '),
|
||||||
}}
|
}}
|
||||||
|
|
||||||
constructor(name = null, visible = true, color = null, labelContent = 'name + value',
|
constructor(name = null, visible = true, color = null, labelContent = 'name + value',
|
||||||
beginIncluded = true, drawLineEnds = true, probabilities = {'0': '0'}, labelPosition = 'above', labelX = 1) {
|
beginIncluded = true, drawLineEnds = true, probabilities = {'0': '0'}, labelPosition = 'above', labelX = 1) {
|
||||||
if(name == null) name = Common.getNewName('XYZUVW')
|
if(name == null) name = Common.getNewName('XYZUVW', "F_")
|
||||||
super(name, visible, color, labelContent)
|
super(name, visible, color, labelContent)
|
||||||
this.beginIncluded = beginIncluded
|
this.beginIncluded = beginIncluded
|
||||||
this.drawLineEnds = drawLineEnds
|
this.drawLineEnds = drawLineEnds
|
||||||
|
@ -56,14 +56,16 @@ class RepartitionFunction extends Common.ExecutableObject {
|
||||||
|
|
||||||
|
|
||||||
getReadableString() {
|
getReadableString() {
|
||||||
var keys = Object.keys(this.probabilities).sort((a,b) => a-b);
|
let keys = Object.keys(this.probabilities).sort((a,b) => a-b);
|
||||||
return `F_${this.name}(x) = P(${this.name} ≤ x)\n` + keys.map(idx => `P(${this.name}=${idx})=${this.probabilities[idx]}`).join("; ")
|
let varname = this.name.substring(this.name.indexOf("_")+1)
|
||||||
|
return `${this.name}(x) = P(${varname} ≤ x)\n` + keys.map(idx => `P(${varname}=${idx})=${this.probabilities[idx]}`).join("; ")
|
||||||
}
|
}
|
||||||
|
|
||||||
getLatexString() {
|
getLatexString() {
|
||||||
let keys = Object.keys(this.probabilities).sort((a,b) => a-b);
|
let keys = Object.keys(this.probabilities).sort((a,b) => a-b);
|
||||||
let varName = Latex.variable(this.name)
|
let funcName = Latex.variable(this.name)
|
||||||
return `\\begin{array}{l}F_{${varName}}(x) = P(${varName} \\le x)\\\\` + keys.map(idx => `P(${varName}=${idx})=${this.probabilities[idx]}`).join("; ") + '\\end{array}'
|
let varName = Latex.variable(this.name.substring(this.name.indexOf("_")+1))
|
||||||
|
return `\\begin{array}{l}{${funcName}}(x) = P(${varName} \\le x)\\\\` + keys.map(idx => `P(${varName}=${idx})=${this.probabilities[idx]}`).join("; ") + '\\end{array}'
|
||||||
}
|
}
|
||||||
|
|
||||||
execute(x = 1) {
|
execute(x = 1) {
|
||||||
|
@ -83,7 +85,7 @@ class RepartitionFunction extends Common.ExecutableObject {
|
||||||
getLabel() {
|
getLabel() {
|
||||||
switch(this.labelContent) {
|
switch(this.labelContent) {
|
||||||
case 'name':
|
case 'name':
|
||||||
return `P(${this.name} ≤ x)`
|
return `${this.name}(x)`
|
||||||
case 'name + value':
|
case 'name + value':
|
||||||
return this.getReadableString()
|
return this.getReadableString()
|
||||||
case 'null':
|
case 'null':
|
||||||
|
|
|
@ -26,7 +26,7 @@
|
||||||
.import "../math/latex.js" as Latex
|
.import "../math/latex.js" as Latex
|
||||||
|
|
||||||
|
|
||||||
class SommeGainsBode extends Common.DrawableObject {
|
class SommeGainsBode extends Common.ExecutableObject {
|
||||||
static type(){return 'Somme gains Bode'}
|
static type(){return 'Somme gains Bode'}
|
||||||
static displayType(){return qsTr('Bode Magnitudes Sum')}
|
static displayType(){return qsTr('Bode Magnitudes Sum')}
|
||||||
static displayTypeMultiple(){return qsTr('Bode Magnitudes Sum')}
|
static displayTypeMultiple(){return qsTr('Bode Magnitudes Sum')}
|
||||||
|
|
|
@ -30,8 +30,8 @@ class Text extends Common.DrawableObject {
|
||||||
static displayType(){return qsTr('Text')}
|
static displayType(){return qsTr('Text')}
|
||||||
static displayTypeMultiple(){return qsTr('Texts')}
|
static displayTypeMultiple(){return qsTr('Texts')}
|
||||||
static properties() {return {
|
static properties() {return {
|
||||||
[QT_TRANSLATE_NOOP('prop','x')]: 'Expression',
|
[QT_TRANSLATE_NOOP('prop','x')]: new P.Expression(),
|
||||||
[QT_TRANSLATE_NOOP('prop','y')]: 'Expression',
|
[QT_TRANSLATE_NOOP('prop','y')]: new P.Expression(),
|
||||||
[QT_TRANSLATE_NOOP('prop','labelPosition')]: P.Enum.Positioning,
|
[QT_TRANSLATE_NOOP('prop','labelPosition')]: P.Enum.Positioning,
|
||||||
[QT_TRANSLATE_NOOP('prop','text')]: 'string',
|
[QT_TRANSLATE_NOOP('prop','text')]: 'string',
|
||||||
'comment1': QT_TRANSLATE_NOOP(
|
'comment1': QT_TRANSLATE_NOOP(
|
||||||
|
|
|
@ -16,6 +16,13 @@
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
class Expression {
|
||||||
|
constructor(...variables) {
|
||||||
|
this.type = 'Expression'
|
||||||
|
this.variables = variables
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class Enum {
|
class Enum {
|
||||||
constructor(...values) {
|
constructor(...values) {
|
||||||
this.type = 'Enum'
|
this.type = 'Enum'
|
||||||
|
|
|
@ -26,3 +26,6 @@ var Input = Common.InputExpression
|
||||||
var TokenType = TK.TokenType
|
var TokenType = TK.TokenType
|
||||||
var Token = TK.Token
|
var Token = TK.Token
|
||||||
var Tokenizer = TK.ExpressionTokenizer
|
var Tokenizer = TK.ExpressionTokenizer
|
||||||
|
|
||||||
|
var FUNCTIONS_LIST = Reference.FUNCTIONS_LIST
|
||||||
|
var CONSTANTS_LIST = Reference.CONSTANTS_LIST
|
||||||
|
|
|
@ -64,6 +64,8 @@ const FUNCTIONS = {
|
||||||
"tan": Math.tan,
|
"tan": Math.tan,
|
||||||
"tanh": Math.tanh,
|
"tanh": Math.tanh,
|
||||||
"trunc": Math.trunc,
|
"trunc": Math.trunc,
|
||||||
|
"integral": () => 0, // TODO: Implement
|
||||||
|
"derivative": () => 0,
|
||||||
}
|
}
|
||||||
const FUNCTIONS_LIST = Object.keys(FUNCTIONS);
|
const FUNCTIONS_LIST = Object.keys(FUNCTIONS);
|
||||||
// TODO: Complete
|
// TODO: Complete
|
||||||
|
|
|
@ -41,9 +41,10 @@ var TokenType = {
|
||||||
}
|
}
|
||||||
|
|
||||||
class Token {
|
class Token {
|
||||||
constructor(type, value) {
|
constructor(type, value, startPosition) {
|
||||||
this.type = type;
|
this.type = type;
|
||||||
this.value = value;
|
this.value = value;
|
||||||
|
this.startPosition = startPosition
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -65,7 +66,7 @@ class ExpressionTokenizer {
|
||||||
while(!this.input.atEnd() && WHITESPACES.includes(this.input.peek())) {
|
while(!this.input.atEnd() && WHITESPACES.includes(this.input.peek())) {
|
||||||
included += this.input.next();
|
included += this.input.next();
|
||||||
}
|
}
|
||||||
return new Token(TokenType.WHITESPACE, included)
|
return new Token(TokenType.WHITESPACE, included, this.input.position-included.length)
|
||||||
}
|
}
|
||||||
|
|
||||||
readString() {
|
readString() {
|
||||||
|
@ -80,7 +81,7 @@ class ExpressionTokenizer {
|
||||||
included += this.input.next();
|
included += this.input.next();
|
||||||
}
|
}
|
||||||
this.input.skip(delimitation)
|
this.input.skip(delimitation)
|
||||||
let token = new Token(TokenType.STRING, included)
|
let token = new Token(TokenType.STRING, included, this.input.position-included.length)
|
||||||
token.limitator = delimitation
|
token.limitator = delimitation
|
||||||
return token
|
return token
|
||||||
} else {
|
} else {
|
||||||
|
@ -98,7 +99,7 @@ class ExpressionTokenizer {
|
||||||
}
|
}
|
||||||
included += this.input.next();
|
included += this.input.next();
|
||||||
}
|
}
|
||||||
return new Token(TokenType.NUMBER, included)
|
return new Token(TokenType.NUMBER, included, this.input.position-included.length)
|
||||||
}
|
}
|
||||||
|
|
||||||
readOperator() {
|
readOperator() {
|
||||||
|
@ -106,7 +107,7 @@ class ExpressionTokenizer {
|
||||||
while(!this.input.atEnd() && OPERATORS.includes(this.input.peek())) {
|
while(!this.input.atEnd() && OPERATORS.includes(this.input.peek())) {
|
||||||
included += this.input.next();
|
included += this.input.next();
|
||||||
}
|
}
|
||||||
return new Token(TokenType.OPERATOR, included)
|
return new Token(TokenType.OPERATOR, included, this.input.position-included.length)
|
||||||
}
|
}
|
||||||
|
|
||||||
readIdentifier() {
|
readIdentifier() {
|
||||||
|
@ -115,11 +116,11 @@ class ExpressionTokenizer {
|
||||||
identifier += this.input.next();
|
identifier += this.input.next();
|
||||||
}
|
}
|
||||||
if(Reference.CONSTANTS_LIST.includes(identifier.toLowerCase())) {
|
if(Reference.CONSTANTS_LIST.includes(identifier.toLowerCase())) {
|
||||||
return new Token(TokenType.CONSTANT, identifier.toLowerCase())
|
return new Token(TokenType.CONSTANT, identifier.toLowerCase(), this.input.position-identifier.length)
|
||||||
} else if(Reference.FUNCTIONS_LIST.includes(identifier.toLowerCase())) {
|
} else if(Reference.FUNCTIONS_LIST.includes(identifier.toLowerCase())) {
|
||||||
return new Token(TokenType.FUNCTION, identifier.toLowerCase())
|
return new Token(TokenType.FUNCTION, identifier.toLowerCase(), this.input.position-identifier.length)
|
||||||
} else {
|
} else {
|
||||||
return new Token(TokenType.VARIABLE, identifier)
|
return new Token(TokenType.VARIABLE, identifier, this.input.position-identifier.length)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -133,12 +134,12 @@ class ExpressionTokenizer {
|
||||||
if(NUMBER_CHARS.includes(c)) return this.readNumber();
|
if(NUMBER_CHARS.includes(c)) return this.readNumber();
|
||||||
if(IDENTIFIER_CHARS.includes(c.toLowerCase())) return this.readIdentifier();
|
if(IDENTIFIER_CHARS.includes(c.toLowerCase())) return this.readIdentifier();
|
||||||
if(OPERATORS.includes(c)) return this.readOperator();
|
if(OPERATORS.includes(c)) return this.readOperator();
|
||||||
if(Reference.CONSTANTS_LIST.includes(c)) return new Token(TokenType.CONSTANT, c);
|
if(Reference.CONSTANTS_LIST.includes(c)) return new Token(TokenType.CONSTANT, this.input.next(), this.input.position-1);
|
||||||
if(PUNCTUTATION.includes(c)) return new Token(TokenType.PUNCT, this.input.next());
|
if(PUNCTUTATION.includes(c)) return new Token(TokenType.PUNCT, this.input.next(), this.input.position-1);
|
||||||
if(this.errorOnUnknown)
|
if(this.errorOnUnknown)
|
||||||
this.input.throw("Unknown token character " + c)
|
this.input.throw("Unknown token character " + c)
|
||||||
else
|
else
|
||||||
return new Token(TokenType.UNKNOWN, this.input.next());
|
return new Token(TokenType.UNKNOWN, this.input.next(), this.input.position-1);
|
||||||
}
|
}
|
||||||
|
|
||||||
peek() {
|
peek() {
|
||||||
|
|
Loading…
Reference in a new issue