Skip to content

Commit

Permalink
Add X++ support (#2339)
Browse files Browse the repository at this point in the history
Co-authored-by: Jean Abou Samra <jean@abou-samra.fr>
  • Loading branch information
andrewschmidt-a and jeanas committed Feb 14, 2023
1 parent 972f618 commit 0ae4bc8
Show file tree
Hide file tree
Showing 5 changed files with 771 additions and 1 deletion.
1 change: 1 addition & 0 deletions AUTHORS
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,7 @@ Other contributors, listed alphabetically, are:
* Matteo Sasso -- Common Lisp lexer
* Joe Schafer -- Ada lexer
* Max Schillinger -- TiddlyWiki5 lexer
* Andrew Schmidt -- X++ lexer
* Ken Schutte -- Matlab lexers
* René Schwaiger -- Rainbow Dash style
* Sebastian Schweizer -- Whiley lexer
Expand Down
1 change: 1 addition & 0 deletions pygments/lexers/_mapping.py
Original file line number Diff line number Diff line change
Expand Up @@ -541,6 +541,7 @@
'XmlPhpLexer': ('pygments.lexers.templates', 'XML+PHP', ('xml+php',), (), ('application/xml+php',)),
'XmlSmartyLexer': ('pygments.lexers.templates', 'XML+Smarty', ('xml+smarty',), (), ('application/xml+smarty',)),
'XorgLexer': ('pygments.lexers.xorg', 'Xorg', ('xorg.conf',), ('xorg.conf',), ()),
'XppLexer': ('pygments.lexers.dotnet', 'X++', ('xpp', 'x++'), ('*.xpp',), ()),
'XsltLexer': ('pygments.lexers.html', 'XSLT', ('xslt',), ('*.xsl', '*.xslt', '*.xpl'), ('application/xsl+xml', 'application/xslt+xml')),
'XtendLexer': ('pygments.lexers.jvm', 'Xtend', ('xtend',), ('*.xtend',), ('text/x-xtend',)),
'XtlangLexer': ('pygments.lexers.lisp', 'xtlang', ('extempore',), ('*.xtm',), ()),
Expand Down
113 changes: 112 additions & 1 deletion pygments/lexers/dotnet.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
from pygments.lexers.html import XmlLexer

__all__ = ['CSharpLexer', 'NemerleLexer', 'BooLexer', 'VbNetLexer',
'CSharpAspxLexer', 'VbNetAspxLexer', 'FSharpLexer']
'CSharpAspxLexer', 'VbNetAspxLexer', 'FSharpLexer', 'XppLexer']


class CSharpLexer(RegexLexer):
Expand Down Expand Up @@ -727,3 +727,114 @@ def analyse_text(text):
result += 0.05

return result

class XppLexer(RegexLexer):

"""
For X++ source code. This is based loosely on the CSharpLexer
.. versionadded:: 2.15.0
"""

name = 'X++'
url = 'https://learn.microsoft.com/en-us/dynamics365/fin-ops-core/dev-itpro/dev-ref/xpp-language-reference'
aliases = ['xpp', 'x++']
filenames = ['*.xpp']

flags = re.MULTILINE

XPP_CHARS = ('@?(?:_|[^' +
uni.allexcept('Lu', 'Ll', 'Lt', 'Lm', 'Lo', 'Nl') + '])' +
'[^' + uni.allexcept('Lu', 'Ll', 'Lt', 'Lm', 'Lo', 'Nl',
'Nd', 'Pc', 'Cf', 'Mn', 'Mc') + ']*');
# Temporary, see
# https://github.com/thatch/regexlint/pull/49
XPP_CHARS = XPP_CHARS.replace('\x00', '\x01')

OPERATORS = (
'<=', '>=', '+=', '-=', '*=', '/=', '!=', '==',
'&&', '||', '>>', '<<', '++', '--', '+', '-', '*',
'/', '%', '&', '|', '^', '<', '>', '?', '!', '~', '=',
)
KEYWORDS = ('abstract','anytype','as','async','asc','at','avg','break','breakpoint','by','byref','case','catch',
'changecompany','client','container','continue','count','crosscompany','default','delegate',
'delete_from','desc','display','div','do','edit','else','element','eventhandler','exists','false','final',
'firstfast','firstonly','firstonly10','firstonly100','firstonly1000','flush','for','forceliterals',
'forcenestedloop','forceplaceholders','forceselectorder','forupdate','from','group','if','insert_recordset',
'interface','is','join','like','maxof','minof','mod','new','next','nofetch','notexists','null','optimisticlock','order',
'outer','pause','pessimisticlock','print','private','protected','public','repeatableread','retry','return',
'reverse','select','server','setting','static','sum','super','switch','tablelock','this','throw','true','try','ttsabort','ttsbegin',
'ttscommit','update_recordset','validtimestate','void','where','while','window')
RUNTIME_FUNCTIONS = ('_duration','abs','acos','any2Date','any2Enum','any2Guid','any2Int','any2Int64','any2Real','any2Str','anytodate',
'anytoenum','anytoguid','anytoint','anytoint64','anytoreal','anytostr','asin','atan','beep','cTerm','char2Num','classIdGet',
'corrFlagGet','corrFlagSet','cos','cosh','curExt','curUserId','date2Num','date2Str','datetime2Str','dayName','dayOfMth',
'dayOfWk','dayOfYr','ddb','decRound','dg','dimOf','endMth','enum2str','exp','exp10','fV','fieldId2Name','fieldId2PName',
'fieldName2Id','frac','funcName','getCurrentPartition','getCurrentPartitionRecId','getPrefix','guid2Str','idg','indexId2Name',
'indexName2Id','int2Str','int642Str','intvMax','intvName','intvNo','intvNorm','log10','logN','match','max','min','mkDate','mthName',
'mthOfYr','newGuid','nextMth','nextQtr','nextYr','num2Char','num2Date','num2Str','pmt','power','prevMth','prevQtr','prevYr',
'prmIsDefault','pt','pv','rate','refPrintAll','round','runAs','sessionId','setPrefix','sin','sinh','sleep','sln','str2Date',
'str2Datetime','str2Enum','str2Guid','str2Int','str2Int64','str2Num','str2Time','strAlpha','strCmp','strColSeq','strDel',
'strFind','strFmt','strIns','strKeep','strLTrim','strLen','strLine','strLwr','strNFind','strPoke','strPrompt','strRTrim',
'strRem','strRep','strScan','strUpr','subStr','syd','systemDateGet','systemDateSet','tableId2Name',
'tableId2PName','tableName2Id','tan','tanh','term','time2Str','timeNow','today','trunc','typeOf','uint2Str','wkOfYr','year')
COMPILE_FUNCTIONS = ('attributeStr','classNum','classStr','configurationKeyNum','configurationKeyStr','dataEntityDataSourceStr','delegateStr',
'dimensionHierarchyLevelStr','dimensionHierarchyStr','dimensionReferenceStr','dutyStr','enumCnt','enumLiteralStr','enumNum','enumStr',
'extendedTypeNum','extendedTypeStr','fieldNum','fieldPName','fieldStr','formControlStr','formDataFieldStr','formDataSourceStr',
'formMethodStr','formStr','identifierStr','indexNum','indexStr','licenseCodeNum','licenseCodeStr','literalStr','maxDate','maxInt',
'measureStr','measurementStr','menuItemActionStr','menuItemDisplayStr','menuItemOutputStr','menuStr','methodStr','minInt','privilegeStr',
'queryDatasourceStr','queryMethodStr','queryStr','reportStr','resourceStr','roleStr','ssrsReportStr','staticDelegateStr','staticMethodStr',
'tableCollectionStr','tableFieldGroupStr','tableMethodStr','tableNum','tablePName','tableStaticMethodStr','tableStr','tileStr','varStr',
'webActionItemStr','webDisplayContentItemStr','webFormStr','webMenuStr','webOutputContentItemStr','webReportStr','webSiteTempStr',
'webStaticFileStr','webUrlItemStr','webWebPartStr','webletItemStr','webpageDefStr','websiteDefStr','workflowApprovalStr',
'workflowCategoryStr','workflowTaskStr','workflowTypeStr')

tokens = {}

tokens = {
'root': [
# method names
(r'(\s*)\b(else|if)\b([^\n])', bygroups(Whitespace, Keyword, using(this))), # ensure that if is not treated like a function
(r'^([ \t]*)((?:' + XPP_CHARS + r'(?:\[\])?\s+)+?)' # return type
r'(' + XPP_CHARS + ')' # method name
r'(\s*)(\()', # signature start
bygroups(Whitespace, using(this), Name.Function, Whitespace,
Punctuation)),
(r'^(\s*)(\[)([^\n]*?)(\])', bygroups(Whitespace, Name.Attribute, Name.Variable.Class, Name.Attribute)),
(r'[^\S\n]+', Whitespace),
(r'(\\)(\n)', bygroups(Text, Whitespace)), # line continuation
(r'//[^\n]*?\n', Comment.Single),
(r'/[*][^\n]*?[*]/', Comment.Multiline),
(r'\n', Whitespace),
(words(OPERATORS), Operator),
(r'=~|!=|==|<<|>>|[-+/*%=<>&^|]', Operator),
(r'[()\[\];:,.#@]', Punctuation),
(r'[{}]', Punctuation),
(r'@"(""|[^"])*"', String),
(r'\$?"(\\\\|\\[^\\]|[^"\\\n])*["\n]', String),
(r"'\\.'|'[^\\]'", String.Char),
(r"[0-9]+(\.[0-9]*)?([eE][+-][0-9]+)?"
r"[flFLdD]?|0[xX][0-9a-fA-F]+[Ll]?", Number),
(words(KEYWORDS, suffix=r'\b'), Keyword),
(r'(boolean|int|int64|str|real|guid|date)\b\??', Keyword.Type),
(r'(class|struct|extends|implements)(\s+)', bygroups(Keyword, Whitespace), 'class'),
(r'('+XPP_CHARS+')(::)', bygroups(Name.Variable.Class, Punctuation)),
(r'(\s*)(\w+)(\s+\w+(,|=)?[^\n]*;)', bygroups(Whitespace, Name.Variable.Class, using(this))), # declaration
# x++ specific function to get field should highlight the classname
(r'(fieldNum\()('+XPP_CHARS+r')(\s*,\s*)('+XPP_CHARS+r')(\s*\))',
bygroups(using(this), Name.Variable.Class, using(this), Name.Property, using(this))),
# x++ specific function to get table should highlight the classname
(r'(tableNum\()('+XPP_CHARS+r')(\s*\))',
bygroups(using(this), Name.Variable.Class, using(this))),
(words(RUNTIME_FUNCTIONS, suffix=r'(?=\()'), Name.Function.Magic),
(words(COMPILE_FUNCTIONS, suffix=r'(?=\()'), Name.Function.Magic),
(XPP_CHARS, Name),
],
'class': [
(XPP_CHARS, Name.Class, '#pop'),
default('#pop'),
],
'namespace': [
(r'(?=\()', Text, '#pop'), # using (resource)
('(' + XPP_CHARS + r'|\.)+', Name.Namespace, '#pop'),
]
}
76 changes: 76 additions & 0 deletions tests/examplefiles/xpp/test.xpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@

public class ExampleSummaryTmpTable extends common
{
const str TestConstStr = "this_is_a_test";
int Test classInt = 0;
/// <summary>
/// Populates the table with data from hcmWorker, aggregating counts of email domains
/// </summary>
/// <param name="directReportsOnly">
/// Whether to get the whole reporting structure or just direct reports
/// </param>
[Hookable(False)]
public void populateTable(boolean directReportsOnly = true)
{
DirPersonBaseEntity dirPerson;

// Set up Query for Course fetch
QueryRun qr = new QueryRun(new Query());
QueryBuildDataSource qdbsWorker = qr.query().addDataSource(tableNum(HcmWorker));

QueryBuildDataSource qdbsDirPersonEntity = qdbsWorker.addDataSource(tableNum(DirPersonBaseEntity));

qdbsDirPersonEntity.joinMode(JoinMode::InnerJoin);
qdbsDirPersonEntity.addLink(fieldNum(HcmWorker, Person), fieldNum(DirPersonBaseEntity, RecId));
qdbsDirPersonEntity.addOrderByField(fieldNum(DirPersonBaseEntity, BirthYear));

// Setup ranges to include only reporting structure
HcmWorker::rangeFilterReports(HcmWorkerLookup::currentWorker(), qdbsWorker , directReportsOnly);

while (qr.next())
{
// Process each record and add to count in each bucket.
dirPerson = qr.get(tableNum(DirPersonBaseEntity));

if (this.BirthYear != dirPerson.BirthYear)
{
//when finished with a particular birth year, write the aggregate values to the tmp table
this.writeCurrentRecord();
}
this.BirthYear = dirPerson.BirthYear;
if (strFind(dirPerson.PrimaryContactEmail, "@gmail.com"))
{
this.Completed++;
}
else if (strFind(dirPerson.PrimaryContactEmail, "@yahoo.com"))
{
this.Yahoo++;
}
else if (strFind(dirPerson.PrimaryContactEmail, "@outlook.com"))
{
this.Outlook++;
}
else
{
this.Other++;
}
this.Total++;
}
// write last record
this.writeCurrentRecord();
}

private void writeCurrentRecord(){
if(this.BirthYear != "")
{
ttsbegin;
this.write();
ttscommit;
}
this.clear();
this.Gmail = 0;
this.Yahoo = 0;
this.Outlook = 0;
this.Other = 0;
}
}

0 comments on commit 0ae4bc8

Please sign in to comment.