mvextensions / mvbasic

MultiValue Basic extension for Visual Studio Code
MIT License
30 stars 16 forks source link

[FEATURE] Add jBASE Language Syntax #9

Closed itsxallwater closed 5 years ago

itsxallwater commented 5 years ago

Is your feature request related to a problem? Please describe. Currently, the client\Syntaxes\MvLanguage.json file contains MVON# Basic syntax. We are looking to include jBASE syntax out of the box to have available as an option within the extension.

Describe the solution you'd like An acceptable pull request for this issue would include a jBASE version of the MvLanguage.json file that can be included in the base package. This pull request need not concern itself with any of the plumbing for the extension to be able to dynamically load that syntax into place, we're simply looking for the syntax definition here.

mikes-zum commented 5 years ago

Mike, Attached is a first pass of the jBASE version of the MVON Language file, in a zip, as JSON is not accepted.

There is a lot more to add, so I will be continuing with index number 78 onwards, probably in alphabetic order.

jBASELanguage.zip

Mike

itsxallwater commented 5 years ago

@mikes-zum this looks great so far! If you're feeling adventurous, as you get closer let me know if you'd like to submit the finished product as a pull request and I can help walk you through that.

Also to anyone following along, I played with GitHub comments and I've worked out how to inline JSON (or any code/text) and also put it behind a collapsible section so if that's of interest to anyone let me know and I'll post a peek behind the curtain.

jBASELanguage.json (click to expand) ```json { "Language": { "Type": "jBASE Basic", "Keywords": [ { "key": "For", "icon": 14, "index": 1, "documentation": "The FOR statement allows the programming of looping constructs within the program. The loop is controlled by a counting variable and may be terminated early by expressions tested after every iteration.", "detail":"FOR var = expression1 TO expression2 {STEP expression3}\r\n\r\n{WHILE | UNTIL expression4}\r\nNEXT {var}" }, { "key": "Open", "icon": 14, "index": 2, "documentation": "The OPEN statement is used to open a file or device to a descriptor variable.\r\nIf the OPEN statement fails, it executes any statements associated with an ELSE clause. If the OPEN is successful it will execute any statements associated with a THEN clause. Note that either one or both of the THEN and ELSE clauses are required.", "detail": "OPEN {expression1,}expression2 TO {variable} {SETTING setvar} THEN|ELSE statements" }, { "key": "Dcount", "icon": 3, "index": 3, "documentation": "The DCOUNT() function counts the number of field elements in a string that are separated by a specified delimiter.", "detail": "DCOUNT(expression1, expression2)" }, { "key": "Date()", "icon": 3, "index": 4, "documentation": "The DATE( ) function returns the date in internal system form. This date is expressed as the number of days since December 31, 1967.", "detail": "DATE()" }, { "key": "Select", "icon": 14, "index": 5, "documentation": "The SELECT statement creates a select list of elements in a specified variable for use with the READNEXT statement.", "detail": "SELECT {variable1} {TO variable2 | listnum} {SETTING setvar}" }, { "key": "ReadNext", "icon": 14, "index": 6, "documentation": "READNEXT retrieves the next element in a list variable.", "detail": "READNEXT variable1, variable2 {FROM variable3} {SETTING setvar} {THEN|ELSE statements}" }, { "key": "Read", "icon": 14, "index": 7, "documentation": "The READ statement allows a program to read a record from a previously opened file into a variable.", "detail": "READ variable1 FROM { variable2,} expression {SETTING setvar} {ON ERROR statements} THEN|ELSE statements" }, { "key": "Readv", "icon": 14, "index": 8, "documentation": "The READV statement allows a program to read a specific field from a record in a previously opened file into a variable.", "detail": "READV variable1 FROM { variable2,} expression1, expression2 {SETTING setvar} {ON ERROR statements} THEN|ELSE statements" }, { "key": "Repeat", "icon": 14, "index": 9, "documentation": "REPEAT causes the loop to start again with the first statement following the LOOP statement.", "detail": "REPEAT" }, { "key": "Crt", "icon": 14, "index": 10, "documentation": "The CRT statement sends data directly to the terminal, even if a PRINTER ON statement is currently active. If the optional ':' is appended, no carriage return is appended to the text", "detail": "CRT | DISPLAY expression {, expression..} {:}" }, { "key": "Dim", "icon": 14, "index": 11, "documentation": "The DIM statement is used to declare arrays to the compiler before they are referenced.", "detail": "DIM{ENSION} variable(number{, number... }){, variable(number {,number...}) ...}" }, { "key": "Debug", "icon": 14, "index": 12, "documentation": "The DEBUG statement causes the executing program to enter the jBC debugger.", "detail": "DEBUG" }, { "key": "Call", "icon": 14, "index": 13, "documentation": "The CALL statement transfers program execution to an external subroutine.", "detail": "CALL {@}subroutine.name {(argument {, argument ... })}" }, { "key": "Trim", "icon": 3, "index": 14, "documentation": "The TRIM statement allows characters to be removed from a string in a number of ways.", "detail": "TRIM(expression1 {, expression2{, expression3}})" }, { "key": "Trimf", "icon": 3, "index": 15, "documentation": "The TRIMF() function is equivalent to TRIM(expression, \" \", \"L\")", "detail": "TRIMF(expression)" }, { "key": "Trimb", "icon": 3, "index": 16, "documentation": "The TRIMB() function is equivalent to TRIM(expression, \" \", \"T\")", "detail": "TRIMB(expression)" }, { "key": "Len", "icon": 3, "index": 17, "documentation": "The LEN function returns the character length of the supplied expression.", "detail": "LEN(expression)" }, { "key": "Mat", "icon": 14, "index": 18, "documentation": "The MAT command is used to either assign every element in a specified array to a single value or to assign the entire contents of one array to another.", "detail": "MAT Array = expression\r\nMAT Array1 = MAT Array2" }, { "key": "Match", "icon": 14, "index": 19, "documentation": "The MATCH or MATCHES function allows pattern matching to be applied to an expression.", "detail": "expression1 MATCH/MATCHES expression2" }, { "key": "Write", "icon": 14, "index": 20, "documentation": "The WRITE statement allows a program to write a record into a previously opened file.", "detail": "WRITE variable1 ON|TO { variable2,} expression {SETTING setvar} {ON ERROR statements}" }, { "key": "Writev", "icon": 14, "index": 21, "documentation": "TThe WRITEV statement allows a program to write a specific field of a record in a previously opened file.", "detail": "WRITEV variable1 ON|TO {variable2,} expression1, expression2 {SETTING setvar} {ON ERROR statements}" }, { "key": "Begin Case", "icon": 14, "index": 22, "documentation": "The CASE structure is bounded by the BEGIN CASE and END CASE statements.", "detail": "BEGIN CASE\r\n\r\n...\r\nEND CASE" }, { "key": "Case", "icon": 14, "index": 23, "documentation": "The CASE statement allows the programmer to execute a particular sequence of instructions based upon the results of a series of test expressions.", "detail": "CASE expression\r\nstatement(s)\r\nCASE expression\r\nstatement(s)\r\n....." }, { "key": "Abs", "icon": 3, "index": 23, "documentation": "The ABS function will return the mathematical absolute of the ()expression.", "detail": "ABS(expression)" }, { "key": "Index", "icon": 3, "index": 25, "documentation": "The INDEX function will return the position of a character or characters within another string.", "detail": "INDEX(expression1, expression2, expression3)" }, { "key": "@Am", "icon": 21, "index": 26, "documentation": "Inserts an attribute mark CHAR(254)", "detail": "@AM" }, { "key": "@Vm", "icon": 21, "index": 27, "documentation": "Inserts a value mark CHAR(253)", "detail": "@VM" }, { "key": "@Svm", "icon": 21, "index": 28, "documentation": "Inserts a subvalue mark CHAR(252)", "detail": "@SVM" }, { "key": "@Tm", "icon": 21, "index": 16, "documentation": "Inserts a text mark CHAR(251)", "detail": "@TM" }, { "key": "Execute", "icon": 3, "index": 30, "documentation": "The EXECUTE or PERFORM statement allows the currently executing program to pause and execute any other UNIX/NT program, including another jBC program or a jBASE command.", "detail": "EXECUTE|PERFORM expression {CAPTURING variable} {RETURNING|SETTING variable}\r\n{PASSLIST expression} {RTNLIST {variable}}\r\n{PASSDATA variable} {RTNDATA variable}" }, { "key": "Loop", "icon": 14, "index": 31, "documentation": "The LOOP construct allows the programmer to specify loops with multiple exit conditions.", "detail": "LOOP statements1 WHILE|UNTIL expression DO statements2 REPEAT" }, { "key": "End Case", "icon": 3, "index": 32, "documentation": "The END CASE statement terminates a CASE block. There should be a corresponding END CASE for each BEGIN CASE.", "detail": "END CASE" }, { "key": "End", "icon": 14, "index": 33, "documentation": "The END statement is used to terminate a program or to conclude a set of jBC statements bounded by a condition.", "detail": "END" }, { "key": "Gosub", "icon": 3, "index": 33, "documentation": "The GOSUB statement causes execution of a local subroutine, after which execution will continue with the next line of code.", "detail": "GOSUB label[:]" }, { "key": "Upcase", "icon": 3, "index": 2, "documentation": "UPCASE converts all lowercase characters in an expression to uppercase characters.", "detail": "UPCASE(expression)" }, { "key": "Downcase", "icon": 3, "index": 35, "documentation": "DOWNCASE converts all uppercase characters in an expression to lowercase characters.", "detail": "DOWNCASE(expression)" }, { "key": "Dquote", "icon": 3, "index": 36, "documentation": "The function will put double quotation mark at the beginning and end of a string", "detail": "DQUOTE(Expression)" }, { "key": "Squote", "icon": 3, "index": 37, "documentation": "The function will put single quotation mark at the beginning and end of a string.", "detail": "DQUOTE(Expression)" }, { "key": "Print", "icon": 14, "index": 38, "documentation": "The PRINT statement sends data directly to the current output device, which will either be the terminal or the printer.", "detail": "PRINT {ON Channel} expression {, expression...} {:}" }, { "key": "Printer On", "icon": 14, "index": 39, "documentation": "PRINTER ON will cause all subsequent output from the PRINT statement to be redirected to the print spooler.", "detail": "PRINTER ON" }, { "key": "Printer Off", "icon": 14, "index": 40, "documentation": "PRINTER OFF will cause all subsequent output from the PRINT statement to be redirected to the terminal device.", "detail": "PRINTER OFF" }, { "key": "Printer Close", "icon": 14, "index": 41, "documentation": "PRINTER CLOSE will act as PRINTER OFF but in addition will close all currently active spool jobs created by the active PRINTER ON statement.\r\nIf the optional ON channel_number is specified then only the print job created on that channel number will be closed.", "detail": "PRINTER CLOSE {ON channel_number}" }, { "key": "Oconv", "icon": 3, "index": 42, "documentation": "The OCONV statement is used to convert internal representations of data to their external form.", "detail": "OCONV(expression1, expression2)" }, { "key": "Iconv", "icon": 3, "index": 43, "documentation": "The ICONV function converts data in external form such as dates to their internal form.", "detail": "ICONV(expression1, expression2)" }, { "key": "Int", "icon": 3, "index": 43, "documentation": "The INT function truncates a numeric value into its nearest integer form.", "detail": "INT(expression)" }, { "key": "$Include", "icon": 14, "index": 45, "documentation": "The $INCLUDE\\$INSERT directive inserts the program/code snippet specified into the current source code. If the optional filename is specified the code is read from that file.", "detail": "$INCLUDE programname\r\n$INCLUDE filename programname\r\n$INSERT programname\r\n$INSERT filename programname" }, { "key": "Next", "icon": 14, "index": 46, "documentation": "The NEXT statement terminates a FOR loop. Control is passed back to the FOR statement and the variable is incremented or decremented.", "detail": "NEXT variable" }, { "key": "Continue", "icon": 14, "index": 47, "documentation": "The CONTINUE statement is the complimentary statement to the BREAK statement without arguments.\r\nThe statement is used within a loop to skip the remaining code in the current iteration and proceed directly to the next iteration.", "detail": "CONTINUE" }, { "key": "Return", "icon": 14, "index": 48, "documentation": "The RETURN statement transfers program execution to the caller of a subroutine/function or to a specific label in the program.", "detail": "RETURN {TO label}\r\nRETURN(expression)" }, { "key": "Exit", "icon": 3, "index": 49, "documentation": "The EXIT statement is used to halt the execution of a program and return a numeric exit code to the parent process. For compatibility with older versions of the language the EXIT statement may be used without an expression. In this case it is synonymous with the BREAK statement.", "detail": "EXIT (expression)\r\nEXIT" }, { "key": "Change", "icon": 3, "index": 50, "documentation": "The CHANGE function operates on a variable and replaces all occurrences of one string with another.", "detail": "CHANGE( variable, expression1, expression2 )" }, { "key": "Lower", "icon": 3, "index": 51, "documentation": "The LOWER function lowers system delimiters in a string to the next lowest delimiter.", "detail": "LOWER(expression)" }, { "key": "Raise", "icon": 3, "index": 52, "documentation": "The RAISE function raises system delimiters in a string to the next highest delimiter.", "detail": "RAISE(Expression)" }, { "key": "Delete", "icon": 3, "index": 53, "documentation": "The DEL or DELETE statement is used to remove a specified element from a dynamic array.", "detail": "DEL variable\r\nDELETE(variable,expression1{, expression2{, expression3})" }, { "key": "Delete", "icon": 14, "index": 54, "documentation": "The DELETE statement is used to delete a record from a jBASE file.", "detail": "DELETE {variable,} expression {SETTING setvar} {ON ERROR statements}" }, { "key": "Convert", "icon": 14, "index": 55, "documentation": "The CONVERT statement converts one or more characters in a string to their corresponding replacement characters.", "detail": "CONVERT expression1 TO expression2 IN expression3" }, { "key": "Transtart", "icon": 14, "index": 56, "documentation": "In transaction processing, this statement is used to mark the beginning of a transaction.", "detail": "TRANSTART {SYNC}{start-text} [THEN statement | ELSE statement]" }, { "key": "Transend", "icon": 14, "index": 57, "documentation": "This statement is used to mark the end of a successfully completed transaction.", "detail": "TRANSEND {end-text} [THEN statement | ELSE statement]" }, { "key": "Transabort", "icon": 14, "index": 58, "documentation": "This statement is used to abort the current transaction and reverse any updates to the database.", "detail": "TRANSABORT {abort-text} [THEN statement | ELSE statement]" }, { "key": "Transquery", "icon": 14, "index": 59, "documentation": "This function is used to detect whether or not a transaction is active on the current process.", "detail": "ROLLBACK" }, { "key": "Readu", "icon": 14, "index": 60, "documentation": "The READU statement allows a program to read a record from a previously opened file into a variable. It respects record locking and locks the specified record for update.", "detail": "READU variable1 FROM {variable2,} expression {SETTING setvar} {ON ERROR statements} {LOCKED statements} THEN|ELSE statements" }, { "key": "Writeu", "icon": 14, "index": 61, "documentation": "The WRITEU statement allows a program to write a record into a previously opened file. An existing record lock will be preserved.", "detail": "WRITEU variable1 ON|TO { variable2,} expression {SETTING setvar} {ON ERROR statements}" }, { "key": "Program", "icon": 14, "index": 62, "documentation": "Performs no function other than to document the source code.", "detail": "PROGRAM progname" }, { "key": "Subroutine", "icon": 14, "index": 63, "documentation": "The SUBROUTINE statement is used at the start of any program that will be called externally by the CALL statement. It also declares any parameters to the compiler.", "detail": "SUB{ROUTINE} Name {({MAT} variable{,{MAT} variable...})}" }, { "key": "End Else", "icon": 14, "index": 64, "documentation": "The END ELSE terminates the THEN portion and begin the ELSE portion of a code block", "detail": "END ELSE" }, { "key": "Readlist", "icon": 14, "index": 65, "documentation": "READLIST allows the program to retrieve a previously stored list (perhaps created with the SAVE-LIST command), into a variable", "detail": "READLIST variable1 FROM expression {SETTING variable2} THEN|ELSE statements" }, { "key": "Until", "icon": 14, "index": 66, "documentation": "The UNTIL statement terminates a FOR or LOOP loop when the specified expression is true. The optional DO is required in a LOOP", "detail": "UNTIL expr {DO}" }, { "key": "While", "icon": 14, "index": 67, "documentation": "The WHILE statement terminates a FOR or LOOP loop when the specified expression is true. The optional DO is required in a LOOP", "detail": "WHILE expr {DO}" }, { "key": "Goto", "icon": 14, "index": 68, "documentation": "The GOTO statement causes program execution to jump to the code at a specified label.", "detail": "GOTO label[:]\r\nGO TO label[:]\r\nGO label[:]" }, { "key": "Unassigned", "icon": 3, "index": 69, "documentation": "The UNASSIGNED function allows a program to determine whether a variable has been assigned a value.", "detail": "UNASSIGNED(variable)" }, { "key": "Release", "icon": 14, "index": 70, "documentation": "The RELEASE statement explicitly releases record locks without updating the records using a WRITE or DELETE and it also clears all execution locks without issuing an UNLOCK.", "detail": "RELEASE {{variable,} expression}" }, { "key": "Stop", "icon": 3, "index": 71, "documentation": "The STOP statement is virtually identical in function to the ABORT statement except that a calling jCL program will not be terminated.", "detail": "STOP {message.number{, expression ...}}" }, { "key": "Equate", "icon": 3, "index": 72, "documentation": "EQUATE is used to declare a symbol equivalent to a literal, variable or simple expression.", "detail": "EQU{ATE} symbol TO expression" }, { "key": "Field", "icon": 3, "index": 73, "documentation": "The FIELD function will return a multi-character delimited field from within a string.", "detail": "FIELD(string, delimiter, occurrence{, extractCount})" }, { "key": "Not", "icon": 3, "index":74, "documentation": "The NOT function is used to invert the Boolean value of an expression. It useful for explicitly testing for a false condition.", "detail": "NOT(Expression)" }, { "key": "Common", "icon": 14, "index": 75, "documentation": "The COMMON statement declares a list of variables and matrices that can be shared among various programs. There can be many common areas including a default, unnamed common area.", "detail": "COMMON {/CommonName/} variable{, variable ... }" }, { "key": "Char", "icon": 3, "index": 76, "documentation": "The CHAR function returns the ASCII character specified by the expression.", "detail": "CHAR(expression)" }, { "key": "FormList", "icon": 14, "index": 77, "documentation": "The FORMLIST statement creates an active select list from a dynamic array.", "detail": "FORMLIST variable1 {TO variable2 | listnum}" } ] } }
mikes-zum commented 5 years ago

Attached is a complete jBASE syntax file, currently without any "jabba" bits. It may need some more work, but is mostly complete.

How would I modify the MVON extension to use this syntax, or should I just use it to replace the MVON one for now?

jBASELanguage.zip

itsxallwater commented 5 years ago

Excellent, thanks @mikes-zum!

As far as using this new syntax, #14 sets the tone for what we'll be looking for to make this configuration driven. But yes for local usage if you wanted to override for now it would be a matter of replacing the \mvbasic\client\Syntaxes\MvLanguage.json file with this one.

mikes-zum commented 5 years ago

An updated version of the jBASE syntax with a TRIMS() function definition added.

jBASELanguage_25Jul2019.zip

andrewcole50 commented 5 years ago

Is the syntax being used correct? While the suggestions appear in Intellisense, they do not work like other languages. I've attached a screenshot of using the PHP function str_replace and how Intellisense works vs the above Iconv. Notice how the function helper stays on screen and finished the parenthesis during typing while it disappears and does not autocomplete with Iconv.

ICONV example php-function-suggest

If there's no opposition, I'd like to go through and make some of the function details more useful. Example: Current Iconv Detail: ICONV(expression1, expression2) Suggested Iconv Detail: ICONV(subject, convCode)

Lastly, is there a reason the keys are in title case as opposed to upper case?

itsxallwater commented 5 years ago

Hey @andrewcole50! Thanks for stopping by, extremely happy to have you joining the conversation here.

On the IntelliSense front, you're absolutely correct. We'll take a look at what can happen to cause the helper to stay until the entirety of the syntax that was matched is received.

On the function details, we're going to try to keep those definitions as standard to the documentation for the respective flavor of Pick for the purposes of the extension. That said, as we're in the thread for jBASE syntax I could see cause to improving even the jBASE documentation to inform on the parameters of the function in the manner you're suggesting. We happen to have one of the principal resources responsible for jBASE documentation contributing to the syntax and language definitions for this very ticket, @mikes-zum, so I'd like to get his opinion on adapting any of those changes.

Worst case, when push comes to shove and you find that the standard documentation can't/won't be amended for a language, we have hooks in the extension to allow you to configure custom word/definition value pairs. If that's not feature complete for the objective of customizing language definition, the important takeaway is that we're aiming to provide that customization ability for end users in these scenarios where what is the official provision is not precisely what you'd like.

Speaking on the casing, there is a setting in the config for using Camel Case for IntelliSense keywords. I'd ask you to try flipping that and reporting back on whether or not it resolves your scenario.

Again, thanks for taking the time to come and ask some great questions! I love seeing the community interaction!

andrewcole50 commented 5 years ago

On the function details, we're going to try to keep those definitions as standard to the documentation for the respective flavor of Pick for the purposes of the extension.

I would say that sounds like a great goal to work towards. Once that goal is completed I would suggest that the project should work towards either improving the upstream flavor's documentation (as it looks like we're going to do with Jbase =D ) or if that's not possible, fixing them within this project anyways in order to have a more useful and intuitive intellisense.

andrewcole50 commented 5 years ago

I took a little bit of time and went through JBC's docs and picked out the ambiguous function examples for functions I was familiar with. I've pasted the original and then my suggested replacements. I wanted to put these here so Mike would see them and can look them over and decide what to implement (if any) and to help get his gears working if some of my suggestions could be better.

CONVERT(expression1, expression2, expression3) => CONVERT(subject, search, replace)

COUNT(expression1, expression2) => COUNT(subject, delim)

DCOUNT(expression1, expression2) => DCOUNT(subject, delim)

EXTRACT(expression1, expression2 {, expression3 {, expression4}}) => EXTRACT(dyn.arr, field.num {, value.num {, subvalue.num}})

GROUP(Expression1, Expression2, Expression3, Expression4) => GROUP(string, delim, occurrence, extractCount)

INDEX(expression1, expression2, expression3) => INDEX(string, search, occurrence)

ICONV(expression1, expression2) => ICONV(subject, conv.code)

INSERT(expression1, expression2{, expression3 {, expression4 }}; expression5) => INSERT(dyn.arr, attr{, val {, subval }}; new.elem)

INPUT {@ (expression1 {, expression2 )}{:} Var{{, expression3}, expression4} {:}{_} {WITH expression5} {FOR expression6 THEN|ELSE statements} => INPUT {@ (col {, row )}{:} Var{{, length}, expression} {:}{_} {WITH delim} {FOR deciseconds THEN|ELSE statements}

LOCATE expression1 IN expression2{<expression3{,expression4}>}, {, expression5} {BY expression6} SETTING Var THEN|ELSE statement(s) => LOCATE string IN subject{<attr{,val}>}, {, field} {BY sort.exp} SETTING Var THEN|ELSE statement(s)

LOCATE(expression1, expression2{,expression3{,expression4}}; Var{; expression6}) THEN|ELSE statements => LOCATE(string, subject{,attr{,val}}; Var{; sort.exp}) THEN|ELSE statements

OCONV(expression1, expression2) => OCONV(subject, conv.code)

OPENPATH expression1 TO {variable} {SETTING setvar} THEN|ELSE statements => OPENPATH filepath TO {variable} {SETTING setvar} THEN|ELSE statements

READV variable1 FROM { variable2,} expression1, expression2 {SETTING setvar} {ON ERROR statements} {THEN|ELSE statements} => READV variable1 FROM { filevar,} record.key, attr {SETTING setvar} {ON ERROR statements} {THEN|ELSE statements}

READU variable1 FROM {variable2,} expression {SETTING setvar} {ON ERROR statements} {LOCKED statements} {THEN|ELSE statements} => READU variable1 FROM {filevar,} record.key {SETTING setvar} {ON ERROR statements} {LOCKED statements} {THEN|ELSE statements}

READVU variable1 FROM { variable2,} expression1, expression2 {SETTING setvar} {ON ERROR statements} {LOCKED statements} {THEN|ELSE statements} => READVU variable1 FROM { filevar,} record.key, attr {SETTING setvar} {ON ERROR statements} {LOCKED statements} {THEN|ELSE statements}

STR(expression1, expression2) => STR(string, int)

SWAP(variable, expression1, expression2) => SWAP(subject, search, replace)

TRIM(expression1 {, expression2{, type}}) => TRIM(subject {, remove{, type}})

WRITE variable1 ON|TO { variable2,} expression {SETTING setvar} {ON ERROR statements} => WRITE variable1 ON|TO { filevar,} recordkey {SETTING setvar} {ON ERROR statements}

WRITEU variable1 ON|TO { variable2,} expression {SETTING setvar} {ON ERROR statements} => WRITEU variable1 ON|TO { filevar,} recordkey {SETTING setvar} {ON ERROR statements}

WRITEV variable1 ON|TO {variable2,} expression1, expression2 {SETTING setvar} {ON ERROR statements} => WRITEV variable1 ON|TO {filevar,} recordkey, attr {SETTING setvar} {ON ERROR statements}

WRITEVU variable1 ON|TO { variable2,} expression1, expression2 {SETTING setvar} {ON ERROR statements} => WRITEVU variable1 ON|TO { filevar,} recordkey, attr {SETTING setvar} {ON ERROR statements}
itsxallwater commented 5 years ago

I really like where you're going with this @andrewcole50! I'm going to bring this up internally because I think you've done a lot of fantastic legwork on this and it'd make a lot of sense for us to adopt.

itsxallwater commented 5 years ago

Changes have been incorporate and will be promoted in next publish to the marketplace.