summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorThomas Aglassinger <roskakori@users.sourceforge.net>2016-10-02 12:55:12 +0200
committerThomas Aglassinger <roskakori@users.sourceforge.net>2016-10-02 12:55:12 +0200
commitbc4380207f90b0c3f8edb77ad0b32ef1d701079f (patch)
tree2fc24f32a1fc6694ed78fdcc4e7ebd6330247e8d
parent56e75b33d66738b072f9f5525f3af4a8ba863d8b (diff)
downloadpygments-bc4380207f90b0c3f8edb77ad0b32ef1d701079f.tar.gz
Added lexer for VBScript.
-rw-r--r--AUTHORS3
-rw-r--r--pygments/lexers/_mapping.py1
-rw-r--r--pygments/lexers/_vbscript_builtins.py275
-rw-r--r--pygments/lexers/basic.py70
-rw-r--r--tests/examplefiles/example.vbs55
-rw-r--r--tests/test_basic.py74
6 files changed, 474 insertions, 4 deletions
diff --git a/AUTHORS b/AUTHORS
index 66377ead..915a747d 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -7,7 +7,8 @@ Other contributors, listed alphabetically, are:
* Sam Aaron -- Ioke lexer
* Ali Afshar -- image formatter
-* Thomas Aglassinger -- Easytrieve, JCL, Rexx and Transact-SQL lexers
+* Thomas Aglassinger -- Easytrieve, JCL, Rexx, Transact-SQL and VBScript
+ lexers
* Muthiah Annamalai -- Ezhil lexer
* Kumar Appaiah -- Debian control lexer
* Andreas Amann -- AppleScript lexer
diff --git a/pygments/lexers/_mapping.py b/pygments/lexers/_mapping.py
index a6097b1c..882ec292 100644
--- a/pygments/lexers/_mapping.py
+++ b/pygments/lexers/_mapping.py
@@ -422,6 +422,7 @@ LEXERS = {
'TypoScriptHtmlDataLexer': ('pygments.lexers.typoscript', 'TypoScriptHtmlData', ('typoscripthtmldata',), (), ()),
'TypoScriptLexer': ('pygments.lexers.typoscript', 'TypoScript', ('typoscript',), ('*.ts', '*.txt'), ('text/x-typoscript',)),
'UrbiscriptLexer': ('pygments.lexers.urbi', 'UrbiScript', ('urbiscript',), ('*.u',), ('application/x-urbiscript',)),
+ 'VBScriptLexer': ('pygments.lexers.basic', 'VBScript', (), ('*.vbs', '*.VBS'), ()),
'VCLLexer': ('pygments.lexers.varnish', 'VCL', ('vcl',), ('*.vcl',), ('text/x-vclsrc',)),
'VCLSnippetLexer': ('pygments.lexers.varnish', 'VCLSnippets', ('vclsnippets', 'vclsnippet'), (), ('text/x-vclsnippet',)),
'VCTreeStatusLexer': ('pygments.lexers.console', 'VCTreeStatus', ('vctreestatus',), (), ()),
diff --git a/pygments/lexers/_vbscript_builtins.py b/pygments/lexers/_vbscript_builtins.py
new file mode 100644
index 00000000..493e5c7c
--- /dev/null
+++ b/pygments/lexers/_vbscript_builtins.py
@@ -0,0 +1,275 @@
+# -*- coding: utf-8 -*-
+"""
+ pygments.lexers._vbscript_builtins
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ These are manually translated lists from
+ http://www.indusoft.com/pdf/VBScript%20Reference.pdf.
+
+ :copyright: Copyright 2006-2015 by the Pygments team, see AUTHORS.
+ :license: BSD, see LICENSE for details.
+"""
+KEYWORDS = [
+ # dim: special rule
+ 'call',
+ 'case',
+ 'class',
+ # const: special rule
+ 'do',
+ 'each',
+ 'else',
+ 'elseif',
+ 'end',
+ 'erase',
+ 'execute',
+ 'function',
+ 'exit',
+ 'for',
+ 'function',
+ 'GetRef',
+ 'global',
+ 'if',
+ 'let',
+ 'loop',
+ 'next',
+ 'new',
+ # option: special rule
+ 'private',
+ 'public',
+ 'redim',
+ 'select',
+ 'set',
+ 'sub',
+ 'then',
+ 'wend',
+ 'while',
+ 'with',
+]
+
+BUILTIN_FUNCTIONS = [
+ 'Abs',
+ 'Array',
+ 'Asc',
+ 'Atn',
+ 'CBool',
+ 'CByte',
+ 'CCur',
+ 'CDate',
+ 'CDbl',
+ 'Chr',
+ 'CInt',
+ 'CLng',
+ 'Cos',
+ 'CreateObject',
+ 'CSng',
+ 'CStr',
+ 'Date',
+ 'DateAdd',
+ 'DateDiff',
+ 'DatePart',
+ 'DateSerial',
+ 'DateValue',
+ 'Day',
+ 'Eval',
+ 'Exp',
+ 'Filter',
+ 'Fix',
+ 'FormatCurrency',
+ 'FormatDateTime',
+ 'FormatNumber',
+ 'FormatPercent',
+ 'GetObject',
+ 'GetLocale',
+ 'Hex',
+ 'Hour',
+ 'InStr',
+ 'inStrRev',
+ 'Int',
+ 'IsArray',
+ 'IsDate',
+ 'IsEmpty',
+ 'IsNull',
+ 'IsNumeric',
+ 'IsObject',
+ 'Join',
+ 'LBound',
+ 'LCase',
+ 'Left',
+ 'Len',
+ 'LoadPicture',
+ 'Log',
+ 'LTrim',
+ 'Mid',
+ 'Minute',
+ 'Month',
+ 'MonthName',
+ 'MsgBox',
+ 'Now',
+ 'Oct',
+ 'Randomize',
+ 'RegExp',
+ 'Replace',
+ 'RGB',
+ 'Right',
+ 'Rnd',
+ 'Round',
+ 'RTrim',
+ 'ScriptEngine',
+ 'ScriptEngineBuildVersion',
+ 'ScriptEngineMajorVersion',
+ 'ScriptEngineMinorVersion',
+ 'Second',
+ 'SetLocale',
+ 'Sgn',
+ 'Space',
+ 'Split',
+ 'Sqr',
+ 'StrComp',
+ 'String',
+ 'StrReverse',
+ 'Tan',
+ 'Time',
+ 'Timer',
+ 'TimeSerial',
+ 'TimeValue',
+ 'Trim',
+ 'TypeName',
+ 'UBound',
+ 'UCase',
+ 'VarType',
+ 'Weekday',
+ 'WeekdayName',
+ 'Year',
+]
+
+BUILTIN_VARIABLES = [
+ 'Debug',
+ 'Dictionary',
+ 'Drive',
+ 'Drives',
+ 'Err',
+ 'File',
+ 'Files',
+ 'FileSystemObject',
+ 'Folder',
+ 'Folders',
+ 'Match',
+ 'Matches',
+ 'RegExp',
+ 'Submatches',
+ 'TextStream',
+]
+
+OPERATORS = [
+ '+',
+ '-',
+ '*',
+ '/',
+ '\\',
+ '^',
+ '|',
+ '<',
+ '<=',
+ '>',
+ '>=',
+ '=',
+ '<>',
+ '&',
+ '$',
+]
+
+OPERATOR_WORDS = [
+ 'mod',
+ 'and',
+ 'or',
+ 'xor',
+ 'eqv',
+ 'imp',
+ 'is',
+ 'not',
+]
+
+BUILTIN_CONSTANTS = [
+ 'vbAbort',
+ 'vbAbortRetryIgnore',
+ 'vbApplicationModal',
+ 'vbArray',
+ 'vbBinaryCompare',
+ 'vbBlack',
+ 'vbBlue',
+ 'vbBoole',
+ 'vbByte',
+ 'vbCancel',
+ 'vbCr',
+ 'vbCritical',
+ 'vbCrLf',
+ 'vbCurrency',
+ 'vbCyan',
+ 'vbDataObject',
+ 'vbDate',
+ 'vbDefaultButton1',
+ 'vbDefaultButton2',
+ 'vbDefaultButton3',
+ 'vbDefaultButton4',
+ 'vbDouble',
+ 'vbEmpty',
+ 'vbError',
+ 'vbExclamation',
+ 'vbFalse',
+ 'vbFirstFullWeek',
+ 'vbFirstJan1',
+ 'vbFormFeed',
+ 'vbFriday',
+ 'vbGeneralDate',
+ 'vbGreen',
+ 'vbIgnore',
+ 'vbInformation',
+ 'vbInteger',
+ 'vbLf',
+ 'vbLong',
+ 'vbLongDate',
+ 'vbLongTime',
+ 'vbMagenta',
+ 'vbMonday',
+ 'vbMsgBoxHelpButton',
+ 'vbMsgBoxRight',
+ 'vbMsgBoxRtlReading',
+ 'vbMsgBoxSetForeground',
+ 'vbNewLine',
+ 'vbNo',
+ 'vbNull',
+ 'vbNullChar',
+ 'vbNullString',
+ 'vbObject',
+ 'vbObjectError',
+ 'vbOK',
+ 'vbOKCancel',
+ 'vbOKOnly',
+ 'vbQuestion',
+ 'vbRed',
+ 'vbRetry',
+ 'vbRetryCancel',
+ 'vbSaturday',
+ 'vbShortDate',
+ 'vbShortTime',
+ 'vbSingle',
+ 'vbString',
+ 'vbSunday',
+ 'vbSystemModal',
+ 'vbTab',
+ 'vbTextCompare',
+ 'vbThursday',
+ 'vbTrue',
+ 'vbTuesday',
+ 'vbUseDefault',
+ 'vbUseSystem',
+ 'vbUseSystem',
+ 'vbVariant',
+ 'vbVerticalTab',
+ 'vbWednesday',
+ 'vbWhite',
+ 'vbYellow',
+ 'vbYes',
+ 'vbYesNo',
+ 'vbYesNoCancel',
+] \ No newline at end of file
diff --git a/pygments/lexers/basic.py b/pygments/lexers/basic.py
index a73ad8b4..09d9bfb9 100644
--- a/pygments/lexers/basic.py
+++ b/pygments/lexers/basic.py
@@ -12,11 +12,13 @@
import re
from pygments.lexer import RegexLexer, bygroups, default, words, include
-from pygments.token import Text, Comment, Operator, Keyword, Name, String, \
- Number, Punctuation
+from pygments.token import Comment, Error, Keyword, Name, Number, \
+ Punctuation, Operator, String, Text, Whitespace
+from pygments.lexers import _vbscript_builtins
+
__all__ = ['BlitzBasicLexer', 'BlitzMaxLexer', 'MonkeyLexer', 'CbmBasicV2Lexer',
- 'QBasicLexer']
+ 'QBasicLexer', 'VBScriptLexer']
class BlitzMaxLexer(RegexLexer):
@@ -498,3 +500,65 @@ class QBasicLexer(RegexLexer):
def analyse_text(text):
if '$DYNAMIC' in text or '$STATIC' in text:
return 0.9
+
+
+class VBScriptLexer(RegexLexer):
+ """
+ VBScript is scripting language that is modeled on Visual Basic.
+
+ .. versionadded:: 2.2
+ """
+ name = 'VBScript'
+ aliases = []
+ filenames = ['*.vbs', '*.VBS']
+ flags = re.IGNORECASE
+
+ tokens = {
+ 'root': [
+ (r"'[^\n]*", Comment.Single),
+ (r'\s+', Whitespace),
+ ('"', String.Double, 'string'),
+ ('&h[0-9a-f]+', Number.Hex),
+ # Float variant 1, for example: 1., 1.e2, 1.2e3
+ (r'[0-9]+\.[0-9]*(e[+-]?[0-9]+)?', Number.Float),
+ (r'\.[0-9]+(e[+-]?[0-9]+)?', Number.Float), # Float variant 2, for example: .1, .1e2
+ (r'[0-9]+e[+-]?[0-9]+', Number.Float), # Float variant 3, for example: 123e45
+ ('\d+', Number.Integer),
+ ('#.+#', String), # date or time value
+ (r'(dim)(\s+)([a-z_][a-z0-9_]*)',
+ bygroups(Keyword.Declaration, Whitespace, Name.Variable), 'dim_more'),
+ (r'(function|sub)(\s+)([a-z_][a-z0-9_]*)',
+ bygroups(Keyword.Declaration, Whitespace, Name.Function)),
+ (r'(class)(\s+)([a-z_][a-z0-9_]*)', bygroups(Keyword.Declaration, Whitespace, Name.Class)),
+ (r'(const)(\s+)([a-z_][a-z0-9_]*)', bygroups(Keyword.Declaration, Whitespace, Name.Constant)),
+ (r'(end)(\s+)(class|function|if|property|sub|with)', bygroups(Keyword, Whitespace, Keyword)),
+ (r'(on)(\s+)(error)(\s+)(goto)(\s+)(0)',
+ bygroups(Keyword, Whitespace, Keyword, Whitespace, Keyword, Whitespace, Number.Integer)),
+ (r'(on)(\s+)(error)(\s+)(resume)(\s+)(next)',
+ bygroups(Keyword, Whitespace, Keyword, Whitespace, Keyword, Whitespace, Keyword)),
+ (r'(option)(\s+)(explicit)', bygroups(Keyword, Whitespace, Keyword)),
+ (r'(property)(\s+)(get|let|set)(\s+)([a-z_][a-z0-9_]*)',
+ bygroups(Keyword.Declaration, Whitespace, Keyword.Declaration, Whitespace, Name.Property)),
+ (r'rem\s.*[^\n]*', Comment.Single),
+ (words(_vbscript_builtins.KEYWORDS, suffix=r'\b'), Keyword),
+ (words(_vbscript_builtins.OPERATORS), Operator),
+ (words(_vbscript_builtins.OPERATOR_WORDS, suffix=r'\b'), Operator.Word),
+ (words(_vbscript_builtins.BUILTIN_CONSTANTS, suffix=r'\b'), Name.Constant),
+ (words(_vbscript_builtins.BUILTIN_FUNCTIONS, suffix=r'\b'), Name.Builtin),
+ (words(_vbscript_builtins.BUILTIN_VARIABLES, suffix=r'\b'), Name.Builtin),
+ (r'[a-z_][a-z0-9_]*', Name),
+ (r'\b_\n', Operator),
+ (words(r'(),.:'), Punctuation),
+ ('.+(\n)?', Error)
+ ],
+ 'dim_more': [
+ (r'(\s*)(,)(\s*)([a-z_][a-z0-9]*)', bygroups(Whitespace, Punctuation, Whitespace, Name.Variable)),
+ default('#pop'),
+ ],
+ 'string': [
+ (r'[^"\n]+', String.Double),
+ (r'\"\"', String.Double),
+ (r'"', String.Double, '#pop'),
+ (r'\n', Error, '#pop'), # Unterminated string
+ ],
+ } \ No newline at end of file
diff --git a/tests/examplefiles/example.vbs b/tests/examplefiles/example.vbs
new file mode 100644
index 00000000..d962b73d
--- /dev/null
+++ b/tests/examplefiles/example.vbs
@@ -0,0 +1,55 @@
+rem VBScript examples
+
+' Various constants of different types
+const someText = "some " & """text"""
+const someInt = 123
+const someHex = &h3110c0d3
+const someFloat = 123.45e-67
+const someDate = #1/2/2016#
+const someTime = #12:34:56 AM#
+const someBool = vbTrue ' -1
+
+' Do some math.
+radius = 1.e2
+area = radius ^ 2 * 3.1315
+a = 17 : b = 23
+c = sqr(a ^2 + b ^ 2)
+
+' Write 10 files.
+For i = 1 to 10
+ createFile( i )
+Next
+
+Public Sub createFile(a)
+ Dim fso, TargetFile
+ TargetPath = "C:\some_" & a & ".tmp"
+ Set fso = CreateObject("Scripting.FileSystemObject")
+ Set TargetFile = fso.CreateTextFile(TargetPath)
+ TargetFile.WriteLine("Hello " & vbCrLf & "world!")
+ TargetFile.Close
+End Sub
+
+' Define a class with a property.
+Class Customer
+ Private m_CustomerName
+
+ Private Sub Class_Initialize
+ m_CustomerName = ""
+ End Sub
+
+ ' CustomerName property.
+ Public Property Get CustomerName
+ CustomerName = m_CustomerName
+ End Property
+
+ Public Property Let CustomerName(custname)
+ m_CustomerName = custname
+ End Property
+End Class
+
+' Special constructs
+Option Explicit
+On Error Resume Next
+On Error Goto 0
+
+' Comment without terminating CR/LF. \ No newline at end of file
diff --git a/tests/test_basic.py b/tests/test_basic.py
new file mode 100644
index 00000000..03d10cd2
--- /dev/null
+++ b/tests/test_basic.py
@@ -0,0 +1,74 @@
+# -*- coding: utf-8 -*-
+"""
+ Pygments Basic lexers tests
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ :copyright: Copyright 2006-2016 by the Pygments team, see AUTHORS.
+ :license: BSD, see LICENSE for details.
+"""
+import unittest
+
+from pygments.lexers.basic import VBScriptLexer
+from pygments.token import Error, Name, Number, Punctuation, String, Whitespace
+
+
+class VBScriptLexerTest(unittest.TestCase):
+
+ def setUp(self):
+ self.lexer = VBScriptLexer()
+
+ def _assert_are_tokens_of_type(self, examples, expected_token_type):
+ for test_number, example in enumerate(examples.split(), 1):
+ token_count = 0
+ for token_type, token_value in self.lexer.get_tokens(example):
+ if token_type != Whitespace:
+ token_count += 1
+ self.assertEqual(
+ token_type, expected_token_type,
+ 'token_type #%d for %s is be %s but must be %s' %
+ (test_number, token_value, token_type, expected_token_type))
+ self.assertEqual(
+ token_count, 1,
+ '%s must yield exactly 1 token instead of %d' %
+ (example, token_count))
+
+ def _assert_tokens_match(self, text, expected_tokens_without_trailing_newline):
+ actual_tokens = tuple(self.lexer.get_tokens(text))
+ if (len(actual_tokens) >= 1) and (actual_tokens[-1] == (Whitespace, '\n')):
+ actual_tokens = tuple(actual_tokens[:-1])
+ self.assertEqual(
+ expected_tokens_without_trailing_newline, actual_tokens,
+ 'text must yield expected tokens: %s' % text)
+
+ def test_can_lex_float(self):
+ self._assert_are_tokens_of_type(
+ '1. 1.e1 .1 1.2 1.2e3 1.2e+3 1.2e-3 1e2', Number.Float)
+ self._assert_tokens_match(
+ '1e2.1e2',
+ ((Number.Float, '1e2'), (Number.Float, '.1e2'))
+ )
+
+ def test_can_reject_almost_float(self):
+ self._assert_tokens_match(
+ '.e1',
+ ((Punctuation, '.'), (Name, 'e1')))
+
+ def test_can_lex_integer(self):
+ self._assert_are_tokens_of_type(
+ '1 23 456', Number.Integer)
+
+ def test_can_lex_names(self):
+ self._assert_are_tokens_of_type(
+ u'thingy thingy123 _thingy _123', Name)
+
+ def test_can_recover_after_unterminated_string(self):
+ self._assert_tokens_match(
+ '"x\nx',
+ ((String.Double, '"'), (String.Double, 'x'), (Error, '\n'), (Name, 'x'))
+ )
+
+ def test_can_recover_from_invalid_character(self):
+ self._assert_tokens_match(
+ 'a;bc\nd',
+ ((Name, 'a'), (Error, ';bc\n'), (Name, 'd'))
+ )