Website Summary Files Commits Branches @commit @diff @stats
diff options
context:
space:
mode:
Commit47e8c6f331525f01f150ba80a3557a18618b0cf5 (patch)
Author
 Georgi Barnev <gbarnev@gmail.com>
2018-11-07 13:05:43 +0200
Committer
 dtrendafilov <trendafilov.dn@gmail.com>
2018-11-07 13:05:43 +0200
Files3fde3f4fc002e4d10c49436d85188d4f3b02e33f
IPL-47e8c6f331525f01f150ba80a3557a18618b0cf5.tar
IPL-47e8c6f331525f01f150ba80a3557a18618b0cf5.tar.gz
IPL-47e8c6f331525f01f150ba80a3557a18618b0cf5.zip
Parent5a4403ef23e17a3da84f1ec9feb3ab294d23c198 (diff)
Homework 1 - C# Lexer and Syntax Highlighter for Javascript (#34)
* Initial lexer structure * Added IInputStream interface for the Lexer and its base implementation in FileInputStream not implemented yet. * Extend InputStream interface * Implement FileInputStream not tested * Lexer almost ready - comments need to be parsed. InputStream ready * Added some TODOs * Add error token type and almost parse comments * Parse multiline comments and fix string parsing to not include the quotes. * Added Syntax Highlighter, added support for more js reserved words, removed error tokens to not break the actual word tokens, fixed comments and string token representation to include its special characters * Initial lexer structure * Added IInputStream interface for the Lexer and its base implementation in FileInputStream not implemented yet. * Extend InputStream interface * Implement FileInputStream not tested * Lexer almost ready - comments need to be parsed. InputStream ready * Added some TODOs * Add error token type and almost parse comments * Parse multiline comments and fix string parsing to not include the quotes. * Added Syntax Highlighter, added support for more js reserved words, removed error tokens to not break the actual word tokens, fixed comments and string token representation to include its special characters * Improve project structure and add tests * Fix physical path structure of the projects * Write tests, fix tokenizer bugs * Fix README format, fix AssemblyInfo name * Escape slashes in readme
28 files changed, 2765 insertions, 0 deletions
-rw-r--r--.gitattributes63
-rw-r--r--.gitignore261
-rw-r--r--homework/81709/.gitignore334
-rw-r--r--homework/81709/JSCompiler.Core.Tests/BaseInputStreamFileTests.cs117
-rw-r--r--homework/81709/JSCompiler.Core.Tests/JSCompiler.Core.Tests.csproj75
-rw-r--r--homework/81709/JSCompiler.Core.Tests/LexerTests.cs576
-rw-r--r--homework/81709/JSCompiler.Core.Tests/Properties/AssemblyInfo.cs20
-rw-r--r--homework/81709/JSCompiler.Core.Tests/packages.config5
-rw-r--r--homework/81709/JSCompiler.Core/BaseInputStream.cs91
-rw-r--r--homework/81709/JSCompiler.Core/IInputStream.cs17
-rw-r--r--homework/81709/JSCompiler.Core/JSCompiler.Core.csproj56
-rw-r--r--homework/81709/JSCompiler.Core/Lexer.cs276
-rw-r--r--homework/81709/JSCompiler.Core/NumToken.cs42
-rw-r--r--homework/81709/JSCompiler.Core/Properties/AssemblyInfo.cs36
-rw-r--r--homework/81709/JSCompiler.Core/ReservedStrings.cs82
-rw-r--r--homework/81709/JSCompiler.Core/Token.cs48
-rw-r--r--homework/81709/JSCompiler.Core/TokenType.cs134
-rw-r--r--homework/81709/JSCompiler.Core/Utils.cs41
-rw-r--r--homework/81709/JSCompiler.Core/WordToken.cs45
-rw-r--r--homework/81709/JSCompiler.SyntaxHighlighter/App.config6
-rw-r--r--homework/81709/JSCompiler.SyntaxHighlighter/JSCompiler.SyntaxHighlighter.csproj62
-rw-r--r--homework/81709/JSCompiler.SyntaxHighlighter/Program.cs36
-rw-r--r--homework/81709/JSCompiler.SyntaxHighlighter/Properties/AssemblyInfo.cs36
-rw-r--r--homework/81709/JSCompiler.SyntaxHighlighter/SyntaxHighlighter.cs187
-rw-r--r--homework/81709/JSCompiler.SyntaxHighlighter/test/test.js23
-rw-r--r--homework/81709/JSCompiler.SyntaxHighlighter/test/testout.html49
-rw-r--r--homework/81709/JSCompiler.sln42
-rw-r--r--homework/81709/README.md5
diff --git a/.gitattributes b/.gitattributes
new file mode 100644
index 0000000..1ff0c42
--- /dev/null
+++ b/.gitattributes
@@ -0,0 +1,63 @@
+###############################################################################
+# Set default behavior to automatically normalize line endings.
+###############################################################################
+* text=auto
+
+###############################################################################
+# Set default behavior for command prompt diff.
+#
+# This is need for earlier builds of msysgit that does not have it on by
+# default for csharp files.
+# Note: This is only used by command line
+###############################################################################
+#*.cs diff=csharp
+
+###############################################################################
+# Set the merge driver for project and solution files
+#
+# Merging from the command prompt will add diff markers to the files if there
+# are conflicts (Merging from VS is not affected by the settings below, in VS
+# the diff markers are never inserted). Diff markers may cause the following
+# file extensions to fail to load in VS. An alternative would be to treat
+# these files as binary and thus will always conflict and require user
+# intervention with every merge. To do so, just uncomment the entries below
+###############################################################################
+#*.sln merge=binary
+#*.csproj merge=binary
+#*.vbproj merge=binary
+#*.vcxproj merge=binary
+#*.vcproj merge=binary
+#*.dbproj merge=binary
+#*.fsproj merge=binary
+#*.lsproj merge=binary
+#*.wixproj merge=binary
+#*.modelproj merge=binary
+#*.sqlproj merge=binary
+#*.wwaproj merge=binary
+
+###############################################################################
+# behavior for image files
+#
+# image files are treated as binary by default.
+###############################################################################
+#*.jpg binary
+#*.png binary
+#*.gif binary
+
+###############################################################################
+# diff behavior for common document formats
+#
+# Convert binary document formats to text before diffing them. This feature
+# is only available from the command line. Turn it on by uncommenting the
+# entries below.
+###############################################################################
+#*.doc diff=astextplain
+#*.DOC diff=astextplain
+#*.docx diff=astextplain
+#*.DOCX diff=astextplain
+#*.dot diff=astextplain
+#*.DOT diff=astextplain
+#*.pdf diff=astextplain
+#*.PDF diff=astextplain
+#*.rtf diff=astextplain
+#*.RTF diff=astextplain
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..3c4efe2
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,261 @@
+## Ignore Visual Studio temporary files, build results, and
+## files generated by popular Visual Studio add-ons.
+
+# User-specific files
+*.suo
+*.user
+*.userosscache
+*.sln.docstates
+
+# User-specific files (MonoDevelop/Xamarin Studio)
+*.userprefs
+
+# Build results
+[Dd]ebug/
+[Dd]ebugPublic/
+[Rr]elease/
+[Rr]eleases/
+x64/
+x86/
+bld/
+[Bb]in/
+[Oo]bj/
+[Ll]og/
+
+# Visual Studio 2015 cache/options directory
+.vs/
+# Uncomment if you have tasks that create the project's static files in wwwroot
+#wwwroot/
+
+# MSTest test Results
+[Tt]est[Rr]esult*/
+[Bb]uild[Ll]og.*
+
+# NUNIT
+*.VisualState.xml
+TestResult.xml
+
+# Build Results of an ATL Project
+[Dd]ebugPS/
+[Rr]eleasePS/
+dlldata.c
+
+# DNX
+project.lock.json
+project.fragment.lock.json
+artifacts/
+
+*_i.c
+*_p.c
+*_i.h
+*.ilk
+*.meta
+*.obj
+*.pch
+*.pdb
+*.pgc
+*.pgd
+*.rsp
+*.sbr
+*.tlb
+*.tli
+*.tlh
+*.tmp
+*.tmp_proj
+*.log
+*.vspscc
+*.vssscc
+.builds
+*.pidb
+*.svclog
+*.scc
+
+# Chutzpah Test files
+_Chutzpah*
+
+# Visual C++ cache files
+ipch/
+*.aps
+*.ncb
+*.opendb
+*.opensdf
+*.sdf
+*.cachefile
+*.VC.db
+*.VC.VC.opendb
+
+# Visual Studio profiler
+*.psess
+*.vsp
+*.vspx
+*.sap
+
+# TFS 2012 Local Workspace
+$tf/
+
+# Guidance Automation Toolkit
+*.gpState
+
+# ReSharper is a .NET coding add-in
+_ReSharper*/
+*.[Rr]e[Ss]harper
+*.DotSettings.user
+
+# JustCode is a .NET coding add-in
+.JustCode
+
+# TeamCity is a build add-in
+_TeamCity*
+
+# DotCover is a Code Coverage Tool
+*.dotCover
+
+# NCrunch
+_NCrunch_*
+.*crunch*.local.xml
+nCrunchTemp_*
+
+# MightyMoose
+*.mm.*
+AutoTest.Net/
+
+# Web workbench (sass)
+.sass-cache/
+
+# Installshield output folder
+[Ee]xpress/
+
+# DocProject is a documentation generator add-in
+DocProject/buildhelp/
+DocProject/Help/*.HxT
+DocProject/Help/*.HxC
+DocProject/Help/*.hhc
+DocProject/Help/*.hhk
+DocProject/Help/*.hhp
+DocProject/Help/Html2
+DocProject/Help/html
+
+# Click-Once directory
+publish/
+
+# Publish Web Output
+*.[Pp]ublish.xml
+*.azurePubxml
+# TODO: Comment the next line if you want to checkin your web deploy settings
+# but database connection strings (with potential passwords) will be unencrypted
+#*.pubxml
+*.publishproj
+
+# Microsoft Azure Web App publish settings. Comment the next line if you want to
+# checkin your Azure Web App publish settings, but sensitive information contained
+# in these scripts will be unencrypted
+PublishScripts/
+
+# NuGet Packages
+*.nupkg
+# The packages folder can be ignored because of Package Restore
+**/packages/*
+# except build/, which is used as an MSBuild target.
+!**/packages/build/
+# Uncomment if necessary however generally it will be regenerated when needed
+#!**/packages/repositories.config
+# NuGet v3's project.json files produces more ignoreable files
+*.nuget.props
+*.nuget.targets
+
+# Microsoft Azure Build Output
+csx/
+*.build.csdef
+
+# Microsoft Azure Emulator
+ecf/
+rcf/
+
+# Windows Store app package directories and files
+AppPackages/
+BundleArtifacts/
+Package.StoreAssociation.xml
+_pkginfo.txt
+
+# Visual Studio cache files
+# files ending in .cache can be ignored
+*.[Cc]ache
+# but keep track of directories ending in .cache
+!*.[Cc]ache/
+
+# Others
+ClientBin/
+~$*
+*~
+*.dbmdl
+*.dbproj.schemaview
+*.jfm
+*.pfx
+*.publishsettings
+node_modules/
+orleans.codegen.cs
+
+# Since there are multiple workflows, uncomment next line to ignore bower_components
+# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
+#bower_components/
+
+# RIA/Silverlight projects
+Generated_Code/
+
+# Backup & report files from converting an old project file
+# to a newer Visual Studio version. Backup files are not needed,
+# because we have git ;-)
+_UpgradeReport_Files/
+Backup*/
+UpgradeLog*.XML
+UpgradeLog*.htm
+
+# SQL Server files
+*.mdf
+*.ldf
+
+# Business Intelligence projects
+*.rdl.data
+*.bim.layout
+*.bim_*.settings
+
+# Microsoft Fakes
+FakesAssemblies/
+
+# GhostDoc plugin setting file
+*.GhostDoc.xml
+
+# Node.js Tools for Visual Studio
+.ntvs_analysis.dat
+
+# Visual Studio 6 build log
+*.plg
+
+# Visual Studio 6 workspace options file
+*.opt
+
+# Visual Studio LightSwitch build output
+**/*.HTMLClient/GeneratedArtifacts
+**/*.DesktopClient/GeneratedArtifacts
+**/*.DesktopClient/ModelManifest.xml
+**/*.Server/GeneratedArtifacts
+**/*.Server/ModelManifest.xml
+_Pvt_Extensions
+
+# Paket dependency manager
+.paket/paket.exe
+paket-files/
+
+# FAKE - F# Make
+.fake/
+
+# JetBrains Rider
+.idea/
+*.sln.iml
+
+# CodeRush
+.cr/
+
+# Python Tools for Visual Studio (PTVS)
+__pycache__/
+*.pyc \ No newline at end of file
diff --git a/homework/81709/.gitignore b/homework/81709/.gitignore
new file mode 100644
index 0000000..f00a0eb
--- /dev/null
+++ b/homework/81709/.gitignore
@@ -0,0 +1,334 @@
+## Ignore Visual Studio temporary files, build results, and
+## files generated by popular Visual Studio add-ons.
+##
+## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
+
+# User-specific files
+*.rsuser
+*.suo
+*.user
+*.userosscache
+*.sln.docstates
+
+# User-specific files (MonoDevelop/Xamarin Studio)
+*.userprefs
+
+# Build results
+[Dd]ebug/
+[Dd]ebugPublic/
+[Rr]elease/
+[Rr]eleases/
+x64/
+x86/
+bld/
+[Bb]in/
+[Oo]bj/
+[Ll]og/
+
+# Visual Studio 2015/2017 cache/options directory
+.vs/
+# Uncomment if you have tasks that create the project's static files in wwwroot
+#wwwroot/
+
+# Visual Studio 2017 auto generated files
+Generated\ Files/
+
+# MSTest test Results
+[Tt]est[Rr]esult*/
+[Bb]uild[Ll]og.*
+
+# NUNIT
+*.VisualState.xml
+TestResult.xml
+
+# Build Results of an ATL Project
+[Dd]ebugPS/
+[Rr]eleasePS/
+dlldata.c
+
+# Benchmark Results
+BenchmarkDotNet.Artifacts/
+
+# .NET Core
+project.lock.json
+project.fragment.lock.json
+artifacts/
+
+# StyleCop
+StyleCopReport.xml
+
+# Files built by Visual Studio
+*_i.c
+*_p.c
+*_h.h
+*.ilk
+*.meta
+*.obj
+*.iobj
+*.pch
+*.pdb
+*.ipdb
+*.pgc
+*.pgd
+*.rsp
+*.sbr
+*.tlb
+*.tli
+*.tlh
+*.tmp
+*.tmp_proj
+*_wpftmp.csproj
+*.log
+*.vspscc
+*.vssscc
+.builds
+*.pidb
+*.svclog
+*.scc
+
+# Chutzpah Test files
+_Chutzpah*
+
+# Visual C++ cache files
+ipch/
+*.aps
+*.ncb
+*.opendb
+*.opensdf
+*.sdf
+*.cachefile
+*.VC.db
+*.VC.VC.opendb
+
+# Visual Studio profiler
+*.psess
+*.vsp
+*.vspx
+*.sap
+
+# Visual Studio Trace Files
+*.e2e
+
+# TFS 2012 Local Workspace
+$tf/
+
+# Guidance Automation Toolkit
+*.gpState
+
+# ReSharper is a .NET coding add-in
+_ReSharper*/
+*.[Rr]e[Ss]harper
+*.DotSettings.user
+
+# JustCode is a .NET coding add-in
+.JustCode
+
+# TeamCity is a build add-in
+_TeamCity*
+
+# DotCover is a Code Coverage Tool
+*.dotCover
+
+# AxoCover is a Code Coverage Tool
+.axoCover/*
+!.axoCover/settings.json
+
+# Visual Studio code coverage results
+*.coverage
+*.coveragexml
+
+# NCrunch
+_NCrunch_*
+.*crunch*.local.xml
+nCrunchTemp_*
+
+# MightyMoose
+*.mm.*
+AutoTest.Net/
+
+# Web workbench (sass)
+.sass-cache/
+
+# Installshield output folder
+[Ee]xpress/
+
+# DocProject is a documentation generator add-in
+DocProject/buildhelp/
+DocProject/Help/*.HxT
+DocProject/Help/*.HxC
+DocProject/Help/*.hhc
+DocProject/Help/*.hhk
+DocProject/Help/*.hhp
+DocProject/Help/Html2
+DocProject/Help/html
+
+# Click-Once directory
+publish/
+
+# Publish Web Output
+*.[Pp]ublish.xml
+*.azurePubxml
+# Note: Comment the next line if you want to checkin your web deploy settings,
+# but database connection strings (with potential passwords) will be unencrypted
+*.pubxml
+*.publishproj
+
+# Microsoft Azure Web App publish settings. Comment the next line if you want to
+# checkin your Azure Web App publish settings, but sensitive information contained
+# in these scripts will be unencrypted
+PublishScripts/
+
+# NuGet Packages
+*.nupkg
+# The packages folder can be ignored because of Package Restore
+**/[Pp]ackages/*
+# except build/, which is used as an MSBuild target.
+!**/[Pp]ackages/build/
+# Uncomment if necessary however generally it will be regenerated when needed
+#!**/[Pp]ackages/repositories.config
+# NuGet v3's project.json files produces more ignorable files
+*.nuget.props
+*.nuget.targets
+
+# Microsoft Azure Build Output
+csx/
+*.build.csdef
+
+# Microsoft Azure Emulator
+ecf/
+rcf/
+
+# Windows Store app package directories and files
+AppPackages/
+BundleArtifacts/
+Package.StoreAssociation.xml
+_pkginfo.txt
+*.appx
+
+# Visual Studio cache files
+# files ending in .cache can be ignored
+*.[Cc]ache
+# but keep track of directories ending in .cache
+!*.[Cc]ache/
+
+# Others
+ClientBin/
+~$*
+*~
+*.dbmdl
+*.dbproj.schemaview
+*.jfm
+*.pfx
+*.publishsettings
+orleans.codegen.cs
+
+# Including strong name files can present a security risk
+# (https://github.com/github/gitignore/pull/2483#issue-259490424)
+#*.snk
+
+# Since there are multiple workflows, uncomment next line to ignore bower_components
+# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
+#bower_components/
+
+# RIA/Silverlight projects
+Generated_Code/
+
+# Backup & report files from converting an old project file
+# to a newer Visual Studio version. Backup files are not needed,
+# because we have git ;-)
+_UpgradeReport_Files/
+Backup*/
+UpgradeLog*.XML
+UpgradeLog*.htm
+ServiceFabricBackup/
+*.rptproj.bak
+
+# SQL Server files
+*.mdf
+*.ldf
+*.ndf
+
+# Business Intelligence projects
+*.rdl.data
+*.bim.layout
+*.bim_*.settings
+*.rptproj.rsuser
+
+# Microsoft Fakes
+FakesAssemblies/
+
+# GhostDoc plugin setting file
+*.GhostDoc.xml
+
+# Node.js Tools for Visual Studio
+.ntvs_analysis.dat
+node_modules/
+
+# Visual Studio 6 build log
+*.plg
+
+# Visual Studio 6 workspace options file
+*.opt
+
+# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
+*.vbw
+
+# Visual Studio LightSwitch build output
+**/*.HTMLClient/GeneratedArtifacts
+**/*.DesktopClient/GeneratedArtifacts
+**/*.DesktopClient/ModelManifest.xml
+**/*.Server/GeneratedArtifacts
+**/*.Server/ModelManifest.xml
+_Pvt_Extensions
+
+# Paket dependency manager
+.paket/paket.exe
+paket-files/
+
+# FAKE - F# Make
+.fake/
+
+# JetBrains Rider
+.idea/
+*.sln.iml
+
+# CodeRush personal settings
+.cr/personal
+
+# Python Tools for Visual Studio (PTVS)
+__pycache__/
+*.pyc
+
+# Cake - Uncomment if you are using it
+# tools/**
+# !tools/packages.config
+
+# Tabs Studio
+*.tss
+
+# Telerik's JustMock configuration file
+*.jmconfig
+
+# BizTalk build output
+*.btp.cs
+*.btm.cs
+*.odx.cs
+*.xsd.cs
+
+# OpenCover UI analysis results
+OpenCover/
+
+# Azure Stream Analytics local run output
+ASALocalRun/
+
+# MSBuild Binary and Structured Log
+*.binlog
+
+# NVidia Nsight GPU debugger configuration file
+*.nvuser
+
+# MFractors (Xamarin productivity tool) working folder
+.mfractor/
+
+# Local History for Visual Studio
+.localhistory/ \ No newline at end of file
diff --git a/homework/81709/JSCompiler.Core.Tests/BaseInputStreamFileTests.cs b/homework/81709/JSCompiler.Core.Tests/BaseInputStreamFileTests.cs
new file mode 100644
index 0000000..ebc557c
--- /dev/null
+++ b/homework/81709/JSCompiler.Core.Tests/BaseInputStreamFileTests.cs
@@ -0,0 +1,117 @@
+using System;
+using System.Text;
+using System.Collections.Generic;
+using System.IO;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+
+namespace JSCompiler.Core.Tests
+{
+ [TestClass]
+ public class BaseInputStreamTests
+ {
+ [TestMethod]
+ public void IsEndOfStream_WhenNotEnd_ReturnsFalse()
+ {
+ byte[] buff = new byte[3] { 32, 14, 25 };
+ MemoryStream memStream = new MemoryStream(buff);
+ BaseInputStream testInputStream = new BaseInputStream(memStream);
+
+ bool isEnd;
+ for(int i = 0; i < 2; i++)
+ {
+ isEnd = testInputStream.IsEndOfStream();
+ testInputStream.Next();
+ Assert.IsFalse(isEnd);
+ }
+ }
+
+ [TestMethod]
+ public void IsEndOfStream_AtBeginningWithoutData_ReturnsTrue()
+ {
+ MemoryStream memStream = new MemoryStream();
+ BaseInputStream testInputStream = new BaseInputStream(memStream);
+
+ bool isEnd = testInputStream.IsEndOfStream();
+
+ Assert.IsTrue(isEnd);
+ }
+
+ [TestMethod]
+ public void IsEndOfStream_WhenEnd_ReturnsTrue()
+ {
+ byte[] buff = new byte[3] { 32, 14, 25 };
+ MemoryStream memStream = new MemoryStream(buff);
+ BaseInputStream testInputStream = new BaseInputStream(memStream);
+
+ for (int i = 0; i < 3; i++, testInputStream.Next());
+ bool isEnd = testInputStream.IsEndOfStream();
+
+ Assert.IsTrue(isEnd);
+ }
+
+ [TestMethod]
+ public void Peek_AtPosition_ReturnsCorrectItemFromStream()
+ {
+ byte[] buff = new byte[8] { 32, 14, 25, 14, 12, 51, 25, 33 };
+ MemoryStream memStream = new MemoryStream(buff);
+ BaseInputStream testInputStream = new BaseInputStream(memStream);
+ char item;
+
+ for (int i = 0; i < 5; i++, testInputStream.Next())
+ {
+ item = testInputStream.Peek();
+ Assert.AreEqual((char)buff[i], item);
+ }
+ }
+
+ [TestMethod]
+ public void Peek_AtEnd_ReturnsEndItem()
+ {
+ byte[] buff = new byte[3] { 32, 14, 25 };
+ MemoryStream memStream = new MemoryStream(buff);
+ BaseInputStream testInputStream = new BaseInputStream(memStream);
+
+ for (int i = 0; i < 3; i++, testInputStream.Next());
+ char last = testInputStream.Peek();
+
+ Assert.AreEqual(0, last);
+ }
+
+ [TestMethod]
+ [ExpectedException(typeof(EndOfStreamException))]
+ public void Next_WhenEndStream_ThrowsEndOfStreamException()
+ {
+ byte[] buff = new byte[3] { 32, 14, 25 };
+ MemoryStream memStream = new MemoryStream(buff);
+ BaseInputStream testInputStream = new BaseInputStream(memStream);
+
+ for (int i = 0; i < 4; i++, testInputStream.Next());
+ }
+
+ [TestMethod]
+ public void Next_WhenBuffersAreSwapped_ReturnsCorrectItem()
+ {
+ byte[] buff = new byte[10] { 32, 14, 25, 14, 23, 42, 16, 21, 15, 76 };
+ MemoryStream memStream = new MemoryStream(buff);
+ BaseInputStream testInputStream = new BaseInputStream(memStream, 3);
+
+ for (int i = 0; i < 10; i++)
+ {
+ char curr = testInputStream.Next();
+ Assert.AreEqual((char)buff[i], curr);
+ }
+ }
+
+ [TestMethod]
+ public void Line_NewLinesTracking_ReturnsCorrectNumberOfLines()
+ {
+ byte[] buff = new byte[10] { (byte) '\n', 32, 14, 25, 14, (byte) '\n', 42, 16, (byte) '\n', (byte)'\n' };
+ MemoryStream memStream = new MemoryStream(buff);
+ BaseInputStream testInputStream = new BaseInputStream(memStream);
+
+ for (int i = 0; i < 10; i++, testInputStream.Next());
+
+ Assert.AreEqual(4, testInputStream.Line);
+ }
+ }
+}
diff --git a/homework/81709/JSCompiler.Core.Tests/JSCompiler.Core.Tests.csproj b/homework/81709/JSCompiler.Core.Tests/JSCompiler.Core.Tests.csproj
new file mode 100644
index 0000000..838feb3
--- /dev/null
+++ b/homework/81709/JSCompiler.Core.Tests/JSCompiler.Core.Tests.csproj
@@ -0,0 +1,75 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="15.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <Import Project="..\packages\MSTest.TestAdapter.1.3.2\build\net45\MSTest.TestAdapter.props" Condition="Exists('..\packages\MSTest.TestAdapter.1.3.2\build\net45\MSTest.TestAdapter.props')" />
+ <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
+ <PropertyGroup>
+ <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+ <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+ <ProjectGuid>{F2211B16-C27C-40D6-A856-8A6B5A3787EF}</ProjectGuid>
+ <OutputType>Library</OutputType>
+ <AppDesignerFolder>Properties</AppDesignerFolder>
+ <RootNamespace>JSCompiler.Core.Tests</RootNamespace>
+ <AssemblyName>JSCompiler.Core.Tests</AssemblyName>
+ <TargetFrameworkVersion>v4.7.1</TargetFrameworkVersion>
+ <FileAlignment>512</FileAlignment>
+ <ProjectTypeGuids>{3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
+ <VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">15.0</VisualStudioVersion>
+ <VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
+ <ReferencePath>$(ProgramFiles)\Common Files\microsoft shared\VSTT\$(VisualStudioVersion)\UITestExtensionPackages</ReferencePath>
+ <IsCodedUITest>False</IsCodedUITest>
+ <TestProjectType>UnitTest</TestProjectType>
+ <NuGetPackageImportStamp>
+ </NuGetPackageImportStamp>
+ </PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
+ <DebugSymbols>true</DebugSymbols>
+ <DebugType>full</DebugType>
+ <Optimize>false</Optimize>
+ <OutputPath>bin\Debug\</OutputPath>
+ <DefineConstants>DEBUG;TRACE</DefineConstants>
+ <ErrorReport>prompt</ErrorReport>
+ <WarningLevel>4</WarningLevel>
+ </PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
+ <DebugType>pdbonly</DebugType>
+ <Optimize>true</Optimize>
+ <OutputPath>bin\Release\</OutputPath>
+ <DefineConstants>TRACE</DefineConstants>
+ <ErrorReport>prompt</ErrorReport>
+ <WarningLevel>4</WarningLevel>
+ </PropertyGroup>
+ <ItemGroup>
+ <Reference Include="Microsoft.VisualStudio.TestPlatform.TestFramework, Version=14.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
+ <HintPath>..\packages\MSTest.TestFramework.1.3.2\lib\net45\Microsoft.VisualStudio.TestPlatform.TestFramework.dll</HintPath>
+ </Reference>
+ <Reference Include="Microsoft.VisualStudio.TestPlatform.TestFramework.Extensions, Version=14.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
+ <HintPath>..\packages\MSTest.TestFramework.1.3.2\lib\net45\Microsoft.VisualStudio.TestPlatform.TestFramework.Extensions.dll</HintPath>
+ </Reference>
+ <Reference Include="System" />
+ <Reference Include="System.Core" />
+ </ItemGroup>
+ <ItemGroup>
+ <Compile Include="BaseInputStreamFileTests.cs" />
+ <Compile Include="LexerTests.cs" />
+ <Compile Include="Properties\AssemblyInfo.cs" />
+ </ItemGroup>
+ <ItemGroup>
+ <None Include="packages.config" />
+ </ItemGroup>
+ <ItemGroup>
+ <ProjectReference Include="..\JSCompiler.Core\JSCompiler.Core.csproj">
+ <Project>{C8DD3D07-6AE9-444B-8BA0-22DC633A9432}</Project>
+ <Name>JSCompiler.Core</Name>
+ </ProjectReference>
+ </ItemGroup>
+ <Import Project="$(VSToolsPath)\TeamTest\Microsoft.TestTools.targets" Condition="Exists('$(VSToolsPath)\TeamTest\Microsoft.TestTools.targets')" />
+ <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
+ <Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
+ <PropertyGroup>
+ <ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
+ </PropertyGroup>
+ <Error Condition="!Exists('..\packages\MSTest.TestAdapter.1.3.2\build\net45\MSTest.TestAdapter.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\MSTest.TestAdapter.1.3.2\build\net45\MSTest.TestAdapter.props'))" />
+ <Error Condition="!Exists('..\packages\MSTest.TestAdapter.1.3.2\build\net45\MSTest.TestAdapter.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\MSTest.TestAdapter.1.3.2\build\net45\MSTest.TestAdapter.targets'))" />
+ </Target>
+ <Import Project="..\packages\MSTest.TestAdapter.1.3.2\build\net45\MSTest.TestAdapter.targets" Condition="Exists('..\packages\MSTest.TestAdapter.1.3.2\build\net45\MSTest.TestAdapter.targets')" />
+</Project> \ No newline at end of file
diff --git a/homework/81709/JSCompiler.Core.Tests/LexerTests.cs b/homework/81709/JSCompiler.Core.Tests/LexerTests.cs
new file mode 100644
index 0000000..92df168
--- /dev/null
+++ b/homework/81709/JSCompiler.Core.Tests/LexerTests.cs
@@ -0,0 +1,576 @@
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using JSCompiler.Core;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.IO;
+using System.Threading.Tasks;
+
+namespace JSCompiler.Core.Tests
+{
+ [TestClass()]
+ public class LexerTests
+ {
+
+ [TestMethod]
+ public void Tokenize_Tab_ReturnsTabToken()
+ {
+ byte[] buff = Encoding.ASCII.GetBytes("\t ");
+ MemoryStream memStream = new MemoryStream(buff);
+ BaseInputStream inputStream = new BaseInputStream(memStream);
+ Lexer lexer = new Lexer(inputStream);
+
+ List<Token> tokens = lexer.Tokenize((line, cause) => Assert.Fail());
+
+ List<Token> expected = new List<Token>
+ {
+ new Token(TokenType.Tab),
+ new Token(TokenType.Whitespace),
+ new Token(TokenType.EOF)
+ };
+
+ CollectionAssert.AreEqual(tokens, expected);
+ }
+
+ [TestMethod]
+ public void Tokenize_SkipCarriageReturn_ReturnsNextToken()
+ {
+ byte[] buff = Encoding.ASCII.GetBytes("\r\r");
+ MemoryStream memStream = new MemoryStream(buff);
+ BaseInputStream inputStream = new BaseInputStream(memStream);
+ Lexer lexer = new Lexer(inputStream);
+
+ List<Token> tokens = lexer.Tokenize((line, cause) => Assert.Fail());
+
+ List<Token> expected = new List<Token>
+ {
+ new Token(TokenType.EOF)
+ };
+
+ CollectionAssert.AreEqual(tokens, expected);
+ }
+
+ [TestMethod]
+ public void Tokenize_WhiteSpace_ReturnsWhitespaceToken()
+ {
+ byte[] buff = Encoding.ASCII.GetBytes(" ");
+ MemoryStream memStream = new MemoryStream(buff);
+ BaseInputStream inputStream = new BaseInputStream(memStream);
+ Lexer lexer = new Lexer(inputStream);
+
+ List<Token> tokens = lexer.Tokenize((line, cause) => Assert.Fail());
+
+ List<Token> expected = new List<Token>
+ {
+ new Token(TokenType.Whitespace),
+ new Token(TokenType.Whitespace),
+ new Token(TokenType.EOF)
+ };
+
+ CollectionAssert.AreEqual(tokens, expected);
+ }
+
+ [TestMethod]
+ public void Tokenize_MultilineComment_ReturnsCommentToken()
+ {
+ byte[] buff = Encoding.ASCII.GetBytes("/* \n\n**\n this is comment */**");
+ MemoryStream memStream = new MemoryStream(buff);
+ BaseInputStream inputStream = new BaseInputStream(memStream);
+ Lexer lexer = new Lexer(inputStream);
+
+ List<Token> tokens = lexer.Tokenize((line, cause) => Assert.Fail());
+ List<Token> expected = new List<Token>
+ {
+ new WordToken("/* \n\n**\n this is comment */", TokenType.Comment),
+ new Token(TokenType.Star),
+ new Token(TokenType.Star),
+ new Token(TokenType.EOF)
+ };
+
+ CollectionAssert.AreEqual(tokens, expected);
+ }
+
+ [TestMethod]
+ public void Tokenize_MultilineCommentUnclosed_ReturnsCommentTokenAndCallsOnError()
+ {
+ byte[] buff = Encoding.ASCII.GetBytes("/* \n\n**\n this is comment ** ////gvj");
+ MemoryStream memStream = new MemoryStream(buff);
+ BaseInputStream inputStream = new BaseInputStream(memStream);
+ Lexer lexer = new Lexer(inputStream);
+
+ List<Token> tokens = lexer.Tokenize((line, cause) => Assert.AreEqual(Lexer.UNCLOSED_MULTILINE_COMMENT, cause));
+ List<Token> expected = new List<Token>
+ {
+ new WordToken("/* \n\n**\n this is comment ** ////gvj", TokenType.Comment),
+ new Token(TokenType.EOF)
+ };
+
+ CollectionAssert.AreEqual(tokens, expected);
+ }
+
+ [TestMethod]
+ public void Tokenize_SingleLineCommentStopOnCarriageReturn_ReturnsCommentToken()
+ {
+ byte[] buff = Encoding.ASCII.GetBytes("// some comment here \r\n*");
+ MemoryStream memStream = new MemoryStream(buff);
+ BaseInputStream inputStream = new BaseInputStream(memStream);
+ Lexer lexer = new Lexer(inputStream);
+
+ List<Token> tokens = lexer.Tokenize((line, cause) => Assert.Fail());
+ List<Token> expected = new List<Token>
+ {
+ new WordToken("// some comment here ", TokenType.Comment),
+ new Token(TokenType.NewLine),
+ new Token(TokenType.Star),
+ new Token(TokenType.EOF)
+ };
+
+ CollectionAssert.AreEqual(tokens, expected);
+ }
+
+ [TestMethod]
+ public void Tokenize_SingleLineCommentStopOnNewLine_ReturnsCommentToken()
+ {
+ byte[] buff = Encoding.ASCII.GetBytes("// some comment here \n\n*");
+ MemoryStream memStream = new MemoryStream(buff);
+ BaseInputStream inputStream = new BaseInputStream(memStream);
+ Lexer lexer = new Lexer(inputStream);
+
+ List<Token> tokens = lexer.Tokenize((line, cause) => Assert.Fail());
+ List<Token> expected = new List<Token>
+ {
+ new WordToken("// some comment here ", TokenType.Comment),
+ new Token(TokenType.NewLine),
+ new Token(TokenType.NewLine),
+ new Token(TokenType.Star),
+ new Token(TokenType.EOF)
+ };
+
+ CollectionAssert.AreEqual(tokens, expected);
+ }
+
+ [TestMethod]
+ public void Tokenize_SingleLineCommentStopOnEndStream_ReturnsCommentToken()
+ {
+ byte[] buff = Encoding.ASCII.GetBytes("// some comment here ");
+ MemoryStream memStream = new MemoryStream(buff);
+ BaseInputStream inputStream = new BaseInputStream(memStream);
+ Lexer lexer = new Lexer(inputStream);
+
+ List<Token> tokens = lexer.Tokenize((line, cause) => Assert.Fail());
+ List<Token> expected = new List<Token>
+ {
+ new WordToken("// some comment here ", TokenType.Comment),
+ new Token(TokenType.EOF)
+ };
+
+ CollectionAssert.AreEqual(tokens, expected);
+ }
+
+ [TestMethod]
+ public void Tokenize_Operators_ReturnsOperatorTokens()
+ {
+ byte[] buff = Encoding.ASCII.GetBytes("(){},.-+;*/%~&^|?:[]=!><\\=====!==!=>=<=--++>><<&&||*=/=%=+=-=<<=>>=&=^=|=");
+ MemoryStream memStream = new MemoryStream(buff);
+ BaseInputStream inputStream = new BaseInputStream(memStream);
+ Lexer lexer = new Lexer(inputStream);
+
+ List<Token> tokens = lexer.Tokenize((line, cause) => Assert.Fail());
+ List<Token> expected = new List<Token>
+ {
+ new Token(TokenType.LeftParen),
+ new Token(TokenType.RightParen),
+ new Token(TokenType.LeftBrace),
+ new Token(TokenType.RightBrace),
+ new Token(TokenType.Comma),
+ new Token(TokenType.Dot),
+ new Token(TokenType.Minus),
+ new Token(TokenType.Plus),
+ new Token(TokenType.Semicolon),
+ new Token(TokenType.Star),
+ new Token(TokenType.Division),
+ new Token(TokenType.Modulo),
+ new Token(TokenType.BitwiseNot),
+ new Token(TokenType.BitwiseAnd),
+ new Token(TokenType.BitwiseXor),
+ new Token(TokenType.BitwiseOr),
+ new Token(TokenType.QuestionMark),
+ new Token(TokenType.Colon),
+ new Token(TokenType.LeftSquareBracket),
+ new Token(TokenType.RightSquareBracket),
+ new Token(TokenType.Equal),
+ new Token(TokenType.Bang),
+ new Token(TokenType.Greater),
+ new Token(TokenType.Less),
+ new Token(TokenType.Backslash),
+
+ new WordToken("===", TokenType.StrictEqual),
+ new WordToken("==", TokenType.EqualEqual),
+ new WordToken("!==", TokenType.StrictNotEqual),
+ new WordToken("!=", TokenType.NotEqual),
+ new WordToken(">=", TokenType.GreaterEqual),
+ new WordToken("<=", TokenType.LessEqual),
+ new WordToken("--", TokenType.MinusMinus),
+ new WordToken("++", TokenType.PlusPlus),
+ new WordToken(">>", TokenType.RightShift),
+ new WordToken("<<", TokenType.LeftShift),
+ new WordToken("&&", TokenType.LogicalAnd),
+ new WordToken("||", TokenType.LogicalOr),
+ new WordToken("*=", TokenType.StarEqual),
+ new WordToken("/=", TokenType.DivideEqual),
+ new WordToken("%=", TokenType.ModuloEqual),
+ new WordToken("+=", TokenType.PlusEqual),
+ new WordToken("-=", TokenType.MinusEqual),
+ new WordToken("<<=", TokenType.LeftShiftEqual),
+ new WordToken(">>=", TokenType.RightShiftEqual),
+ new WordToken("&=", TokenType.BitwiseAndEqual),
+ new WordToken("^=", TokenType.BitwiseXorEqual),
+ new WordToken("|=", TokenType.BitwiseOrEqual),
+ new Token(TokenType.EOF)
+ };
+
+ CollectionAssert.AreEqual(tokens, expected);
+ }
+
+ [TestMethod]
+ public void Tokenize_StringDoubleQuotes_ReturnsStringToken()
+ {
+ byte[] buff = Encoding.ASCII.GetBytes(" \"This is string\" ");
+ MemoryStream memStream = new MemoryStream(buff);
+ BaseInputStream inputStream = new BaseInputStream(memStream);
+ Lexer lexer = new Lexer(inputStream);
+
+ List<Token> tokens = lexer.Tokenize((line, cause) => Assert.Fail());
+ List<Token> expected = new List<Token>
+ {
+ new Token(TokenType.Whitespace),
+ new WordToken("\"This is string\"", TokenType.String),
+ new Token(TokenType.Whitespace),
+ new Token(TokenType.EOF)
+ };
+
+ CollectionAssert.AreEqual(tokens, expected);
+ }
+
+ [TestMethod]
+ public void Tokenize_StringDoubleQuotesUnclosed_ReturnsStringTokenAndCallsOnError()
+ {
+ byte[] buff = Encoding.ASCII.GetBytes(" \"This is string ");
+ MemoryStream memStream = new MemoryStream(buff);
+ BaseInputStream inputStream = new BaseInputStream(memStream);
+ Lexer lexer = new Lexer(inputStream);
+
+ List<Token> tokens = lexer.Tokenize((line, cause) => Assert.AreEqual(cause, Lexer.UNCLOSED_STRING));
+ List<Token> expected = new List<Token>
+ {
+ new Token(TokenType.Whitespace),
+ new WordToken("\"This is string ", TokenType.Error),
+ new Token(TokenType.EOF)
+ };
+
+ CollectionAssert.AreEqual(tokens, expected);
+ }
+
+ [TestMethod]
+ public void Tokenize_StringDoubleQuotesUnclosedNewLine_ReturnsStringTokenAndCallsOnError()
+ {
+ byte[] buff = Encoding.ASCII.GetBytes(" \"This is string \n \" \r\n");
+ MemoryStream memStream = new MemoryStream(buff);
+ BaseInputStream inputStream = new BaseInputStream(memStream);
+ Lexer lexer = new Lexer(inputStream);
+
+ List<Token> tokens = lexer.Tokenize((line, cause) => Assert.AreEqual(cause, Lexer.UNCLOSED_STRING_NEWLINE));
+ List<Token> expected = new List<Token>
+ {
+ new Token(TokenType.Whitespace),
+ new WordToken("\"This is string ", TokenType.Error),
+ new Token(TokenType.NewLine),
+ new Token(TokenType.Whitespace),
+ new WordToken("\" ", TokenType.Error),
+ new Token(TokenType.NewLine),
+ new Token(TokenType.EOF)
+ };
+
+ CollectionAssert.AreEqual(tokens, expected);
+ }
+
+ [TestMethod]
+ public void Tokenize_StringDoubleQuotesEscapeNewLine_ReturnsStringToken()
+ {
+ byte[] buff = Encoding.ASCII.GetBytes(" \"This is string \\\r\n \\\n +\"");
+ MemoryStream memStream = new MemoryStream(buff);
+ BaseInputStream inputStream = new BaseInputStream(memStream);
+ Lexer lexer = new Lexer(inputStream);
+
+ List<Token> tokens = lexer.Tokenize((line, cause) => Assert.Fail());
+ List<Token> expected = new List<Token>
+ {
+ new Token(TokenType.Whitespace),
+ new WordToken("\"This is string \\\r\n \\\n +\"", TokenType.String),
+ new Token(TokenType.EOF)
+ };
+
+ CollectionAssert.AreEqual(tokens, expected);
+ }
+
+ [TestMethod]
+ public void Tokenize_StringDoubleQuotesEscapeDoubleQuote_ReturnsStringToken()
+ {
+ byte[] buff = Encoding.ASCII.GetBytes(" \"This is string \\\"inner\\\" \"");
+ MemoryStream memStream = new MemoryStream(buff);
+ BaseInputStream inputStream = new BaseInputStream(memStream);
+ Lexer lexer = new Lexer(inputStream);
+
+ List<Token> tokens = lexer.Tokenize((line, cause) => Assert.Fail());
+ List<Token> expected = new List<Token>
+ {
+ new Token(TokenType.Whitespace),
+ new WordToken("\"This is string \\\"inner\\\" \"", TokenType.String),
+ new Token(TokenType.EOF)
+ };
+
+ CollectionAssert.AreEqual(tokens, expected);
+ }
+
+ [TestMethod]
+ public void Tokenize_StringDoubleQuotesEscapeBackslash_ReturnsStringToken()
+ {
+ byte[] buff = Encoding.ASCII.GetBytes(" \"This is string \\\\ or this \\ \"");
+ MemoryStream memStream = new MemoryStream(buff);
+ BaseInputStream inputStream = new BaseInputStream(memStream);
+ Lexer lexer = new Lexer(inputStream);
+
+ List<Token> tokens = lexer.Tokenize((line, cause) => Assert.Fail());
+ List<Token> expected = new List<Token>
+ {
+ new Token(TokenType.Whitespace),
+ new WordToken("\"This is string \\\\ or this \\ \"", TokenType.String),
+ new Token(TokenType.EOF)
+ };
+
+ CollectionAssert.AreEqual(tokens, expected);
+ }
+
+ [TestMethod]
+ public void Tokenize_StringDoubleQuotesSingleQuotesCombination_ReturnsStringToken()
+ {
+ byte[] buff = Encoding.ASCII.GetBytes(" \"This is string 'inner'\"");
+ MemoryStream memStream = new MemoryStream(buff);
+ BaseInputStream inputStream = new BaseInputStream(memStream);
+ Lexer lexer = new Lexer(inputStream);
+
+ List<Token> tokens = lexer.Tokenize((line, cause) => Assert.Fail());
+ List<Token> expected = new List<Token>
+ {
+ new Token(TokenType.Whitespace),
+ new WordToken("\"This is string 'inner'\"", TokenType.String),
+ new Token(TokenType.EOF)
+ };
+
+ CollectionAssert.AreEqual(tokens, expected);
+ }
+
+ [TestMethod]
+ public void Tokenize_StringSingleQuotes_ReturnsStringToken()
+ {
+ byte[] buff = Encoding.ASCII.GetBytes(" 'This is string' ");
+ MemoryStream memStream = new MemoryStream(buff);
+ BaseInputStream inputStream = new BaseInputStream(memStream);
+ Lexer lexer = new Lexer(inputStream);
+
+ List<Token> tokens = lexer.Tokenize((line, cause) => Assert.Fail());
+ List<Token> expected = new List<Token>
+ {
+ new Token(TokenType.Whitespace),
+ new WordToken("'This is string'", TokenType.String),
+ new Token(TokenType.Whitespace),
+ new Token(TokenType.EOF)
+ };
+
+ CollectionAssert.AreEqual(tokens, expected);
+ }
+
+ [TestMethod]
+ public void Tokenize_StringSingleQuotesUnclosed_ReturnsStringTokenAndCallsOnError()
+ {
+ byte[] buff = Encoding.ASCII.GetBytes(" 'This is string ");
+ MemoryStream memStream = new MemoryStream(buff);
+ BaseInputStream inputStream = new BaseInputStream(memStream);
+ Lexer lexer = new Lexer(inputStream);
+
+ List<Token> tokens = lexer.Tokenize((line, cause) => Assert.AreEqual(cause, Lexer.UNCLOSED_STRING));
+ List<Token> expected = new List<Token>
+ {
+ new Token(TokenType.Whitespace),
+ new WordToken("'This is string ", TokenType.Error),
+ new Token(TokenType.EOF)
+ };
+
+ CollectionAssert.AreEqual(tokens, expected);
+ }
+
+ [TestMethod]
+ public void Tokenize_StringSingleQuotesUnclosedNewLine_ReturnsStringTokenAndCallsOnError()
+ {
+ byte[] buff = Encoding.ASCII.GetBytes(" 'This is string \n ' \r\n");
+ MemoryStream memStream = new MemoryStream(buff);
+ BaseInputStream inputStream = new BaseInputStream(memStream);
+ Lexer lexer = new Lexer(inputStream);
+
+ List<Token> tokens = lexer.Tokenize((line, cause) => Assert.AreEqual(cause, Lexer.UNCLOSED_STRING_NEWLINE));
+ List<Token> expected = new List<Token>
+ {
+ new Token(TokenType.Whitespace),
+ new WordToken("'This is string ", TokenType.Error),
+ new Token(TokenType.NewLine),
+ new Token(TokenType.Whitespace),
+ new WordToken("' ", TokenType.Error),
+ new Token(TokenType.NewLine),
+ new Token(TokenType.EOF)
+ };
+
+ CollectionAssert.AreEqual(tokens, expected);
+ }
+
+ [TestMethod]
+ public void Tokenize_StringSingleQuotesEscapeNewLine_ReturnsStringToken()
+ {
+ byte[] buff = Encoding.ASCII.GetBytes(" 'This is string \\\r\n \\\n +'");
+ MemoryStream memStream = new MemoryStream(buff);
+ BaseInputStream inputStream = new BaseInputStream(memStream);
+ Lexer lexer = new Lexer(inputStream);
+
+ List<Token> tokens = lexer.Tokenize((line, cause) => Assert.Fail());
+ List<Token> expected = new List<Token>
+ {
+ new Token(TokenType.Whitespace),
+ new WordToken("'This is string \\\r\n \\\n +'", TokenType.String),
+ new Token(TokenType.EOF)
+ };
+
+ CollectionAssert.AreEqual(tokens, expected);
+ }
+
+ [TestMethod]
+ public void Tokenize_StringSingleQuotesEscapeDoubleQuote_ReturnsStringToken()
+ {
+ byte[] buff = Encoding.ASCII.GetBytes(" 'This is string \\'inner\\' '");
+ MemoryStream memStream = new MemoryStream(buff);
+ BaseInputStream inputStream = new BaseInputStream(memStream);
+ Lexer lexer = new Lexer(inputStream);
+
+ List<Token> tokens = lexer.Tokenize((line, cause) => Assert.Fail());
+ List<Token> expected = new List<Token>
+ {
+ new Token(TokenType.Whitespace),
+ new WordToken("'This is string \\'inner\\' '", TokenType.String),
+ new Token(TokenType.EOF)
+ };
+
+ CollectionAssert.AreEqual(tokens, expected);
+ }
+
+ [TestMethod]
+ public void Tokenize_StringSingleQuotesEscapeBackslash_ReturnsStringToken()
+ {
+ byte[] buff = Encoding.ASCII.GetBytes(" 'This is string \\\\ or this \\ '");
+ MemoryStream memStream = new MemoryStream(buff);
+ BaseInputStream inputStream = new BaseInputStream(memStream);
+ Lexer lexer = new Lexer(inputStream);
+
+ List<Token> tokens = lexer.Tokenize((line, cause) => Assert.Fail());
+ List<Token> expected = new List<Token>
+ {
+ new Token(TokenType.Whitespace),
+ new WordToken("'This is string \\\\ or this \\ '", TokenType.String),
+ new Token(TokenType.EOF)
+ };
+
+ CollectionAssert.AreEqual(tokens, expected);
+ }
+
+ [TestMethod]
+ public void Tokenize_StringSingleQuotesSingleQuotesCombination_ReturnsStringToken()
+ {
+ byte[] buff = Encoding.ASCII.GetBytes(" 'This is string \"inner\"'");
+ MemoryStream memStream = new MemoryStream(buff);
+ BaseInputStream inputStream = new BaseInputStream(memStream);
+ Lexer lexer = new Lexer(inputStream);
+
+ List<Token> tokens = lexer.Tokenize((line, cause) => Assert.Fail());
+ List<Token> expected = new List<Token>
+ {
+ new Token(TokenType.Whitespace),
+ new WordToken("'This is string \"inner\"'", TokenType.String),
+ new Token(TokenType.EOF)
+ };
+
+ CollectionAssert.AreEqual(tokens, expected);
+ }
+
+ [TestMethod]
+ public void Tokenize_Number_ReturnsNumberToken()
+ {
+ byte[] buff = Encoding.ASCII.GetBytes(" 1234");
+ MemoryStream memStream = new MemoryStream(buff);
+ BaseInputStream inputStream = new BaseInputStream(memStream);
+ Lexer lexer = new Lexer(inputStream);
+
+ List<Token> tokens = lexer.Tokenize((line, cause) => Assert.Fail());
+ List<Token> expected = new List<Token>
+ {
+ new Token(TokenType.Whitespace),
+ new NumToken(1234),
+ new Token(TokenType.EOF)
+ };
+
+ CollectionAssert.AreEqual(tokens, expected);
+ }
+
+ [TestMethod]
+ public void Tokenize_ReservedOrIdentifier_ReturnsReservedToken()
+ {
+ byte[] buff = Encoding.ASCII.GetBytes(" Object var someIdentifier132 ");
+ MemoryStream memStream = new MemoryStream(buff);
+ BaseInputStream inputStream = new BaseInputStream(memStream);
+ Lexer lexer = new Lexer(inputStream);
+
+ List<Token> tokens = lexer.Tokenize((line, cause) => Assert.Fail());
+ List<Token> expected = new List<Token>
+ {
+ new Token(TokenType.Whitespace),
+ new WordToken("Object", TokenType.Object),
+ new Token(TokenType.Whitespace),
+ new WordToken("var", TokenType.Var),
+ new Token(TokenType.Whitespace),
+ new WordToken("someIdentifier132", TokenType.Identifier),
+ new Token(TokenType.Whitespace),
+ new Token(TokenType.EOF)
+ };
+
+ CollectionAssert.AreEqual(tokens, expected);
+ }
+
+ [TestMethod]
+ public void Tokenize_UnresolvedSymbol_ReturnsUnidentifiedTokenAndCallsOnError()
+ {
+ byte[] buff = Encoding.ASCII.GetBytes(" Object `");
+ MemoryStream memStream = new MemoryStream(buff);
+ BaseInputStream inputStream = new BaseInputStream(memStream);
+ Lexer lexer = new Lexer(inputStream);
+
+ List<Token> tokens = lexer.Tokenize((line, cause) => Assert.AreEqual(cause, Lexer.UNIDENTIFIED_SYMBOL));
+ List<Token> expected = new List<Token>
+ {
+ new Token(TokenType.Whitespace),
+ new WordToken("Object", TokenType.Object),
+ new Token(TokenType.Whitespace),
+ new WordToken("`", TokenType.Error),
+ new Token(TokenType.EOF)
+ };
+
+ CollectionAssert.AreEqual(tokens, expected);
+ }
+ }
+} \ No newline at end of file
diff --git a/homework/81709/JSCompiler.Core.Tests/Properties/AssemblyInfo.cs b/homework/81709/JSCompiler.Core.Tests/Properties/AssemblyInfo.cs
new file mode 100644
index 0000000..8cbad7f
--- /dev/null
+++ b/homework/81709/JSCompiler.Core.Tests/Properties/AssemblyInfo.cs
@@ -0,0 +1,20 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+[assembly: AssemblyTitle("JSCompiler.Core.Tests")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("JSCompiler.Core.Tests")]
+[assembly: AssemblyCopyright("Copyright © 2018")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+[assembly: ComVisible(false)]
+
+[assembly: Guid("f2211b16-c27c-40d6-a856-8a6b5a3787ef")]
+
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]
diff --git a/homework/81709/JSCompiler.Core.Tests/packages.config b/homework/81709/JSCompiler.Core.Tests/packages.config
new file mode 100644
index 0000000..1eae809
--- /dev/null
+++ b/homework/81709/JSCompiler.Core.Tests/packages.config
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<packages>
+ <package id="MSTest.TestAdapter" version="1.3.2" targetFramework="net471" />
+ <package id="MSTest.TestFramework" version="1.3.2" targetFramework="net471" />
+</packages> \ No newline at end of file
diff --git a/homework/81709/JSCompiler.Core/BaseInputStream.cs b/homework/81709/JSCompiler.Core/BaseInputStream.cs
new file mode 100644
index 0000000..7145ce8
--- /dev/null
+++ b/homework/81709/JSCompiler.Core/BaseInputStream.cs
@@ -0,0 +1,91 @@
+using System;
+using System.IO;
+using System.Collections;
+using System.Collections.Generic;
+
+namespace JSCompiler.Core
+{
+ public class BaseInputStream : IInputStream
+ {
+ private char[][] buffs;
+
+ private int index;
+
+ private int activeBuffer;
+
+ private StreamReader reader;
+
+ public BaseInputStream(Stream stream, int buffLength = 4096)
+ {
+ this.Line = 0;
+ this.index = 0;
+ this.activeBuffer = 0;
+ this.buffs = new char[2][];
+ this.buffs[0] = new char[buffLength];
+ this.buffs[1] = new char[buffLength];
+ this.reader = new StreamReader(stream);
+
+ ReadStream(buffs[0], buffLength);
+ }
+
+ public BaseInputStream(string path, int buffLength = 4096)
+ : this(new FileStream(path, FileMode.Open), buffLength) { }
+
+ public int Line { get; private set; }
+
+ public void Close()
+ {
+ reader.Close();
+ }
+
+ public bool IsEndOfStream()
+ {
+ return Peek() == 0;
+ }
+
+ public char Next()
+ {
+ if (IsEndOfStream()) throw new EndOfStreamException();
+
+ char cur = Peek();
+
+ if (cur == '\n')
+ {
+ Line++;
+ }
+
+ index++;
+
+ if (index >= buffs[activeBuffer].Length)
+ {
+ SwitchBuffers();
+ }
+
+ return cur;
+ }
+
+ public char Peek()
+ {
+ return buffs[activeBuffer][index];
+ }
+
+ private void SwitchBuffers()
+ {
+ index = 0;
+ activeBuffer = activeBuffer == 0 ? 1 : 0;
+ ReadStream(buffs[activeBuffer], buffs[activeBuffer].Length);
+ }
+
+ private int ReadStream(char[] buffer, int length)
+ {
+ int bytesRead = reader.Read(buffer, 0, length);
+
+ if (bytesRead < length)
+ {
+ buffer[bytesRead] = (char) 0;
+ }
+
+ return bytesRead;
+ }
+ }
+}
diff --git a/homework/81709/JSCompiler.Core/IInputStream.cs b/homework/81709/JSCompiler.Core/IInputStream.cs
new file mode 100644
index 0000000..ced31d3
--- /dev/null
+++ b/homework/81709/JSCompiler.Core/IInputStream.cs
@@ -0,0 +1,17 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace JSCompiler.Core
+{
+ public interface IInputStream
+ {
+ void Close();
+ bool IsEndOfStream();
+ char Next();
+ char Peek();
+ int Line { get; }
+ }
+}
diff --git a/homework/81709/JSCompiler.Core/JSCompiler.Core.csproj b/homework/81709/JSCompiler.Core/JSCompiler.Core.csproj
new file mode 100644
index 0000000..7b10dd1
--- /dev/null
+++ b/homework/81709/JSCompiler.Core/JSCompiler.Core.csproj
@@ -0,0 +1,56 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
+ <PropertyGroup>
+ <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+ <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+ <ProjectGuid>{C8DD3D07-6AE9-444B-8BA0-22DC633A9432}</ProjectGuid>
+ <OutputType>Library</OutputType>
+ <AppDesignerFolder>Properties</AppDesignerFolder>
+ <RootNamespace>JSCompiler.Core</RootNamespace>
+ <AssemblyName>JSCompiler.Core</AssemblyName>
+ <TargetFrameworkVersion>v4.7.1</TargetFrameworkVersion>
+ <FileAlignment>512</FileAlignment>
+ <Deterministic>true</Deterministic>
+ </PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
+ <DebugSymbols>true</DebugSymbols>
+ <DebugType>full</DebugType>
+ <Optimize>false</Optimize>
+ <OutputPath>bin\Debug\</OutputPath>
+ <DefineConstants>DEBUG;TRACE</DefineConstants>
+ <ErrorReport>prompt</ErrorReport>
+ <WarningLevel>4</WarningLevel>
+ </PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
+ <DebugType>pdbonly</DebugType>
+ <Optimize>true</Optimize>
+ <OutputPath>bin\Release\</OutputPath>
+ <DefineConstants>TRACE</DefineConstants>
+ <ErrorReport>prompt</ErrorReport>
+ <WarningLevel>4</WarningLevel>
+ </PropertyGroup>
+ <ItemGroup>
+ <Reference Include="System" />
+ <Reference Include="System.Core" />
+ <Reference Include="System.Xml.Linq" />
+ <Reference Include="System.Data.DataSetExtensions" />
+ <Reference Include="Microsoft.CSharp" />
+ <Reference Include="System.Data" />
+ <Reference Include="System.Net.Http" />
+ <Reference Include="System.Xml" />
+ </ItemGroup>
+ <ItemGroup>
+ <Compile Include="IInputStream.cs" />
+ <Compile Include="BaseInputStream.cs" />
+ <Compile Include="Lexer.cs" />
+ <Compile Include="NumToken.cs" />
+ <Compile Include="Properties\AssemblyInfo.cs" />
+ <Compile Include="ReservedStrings.cs" />
+ <Compile Include="Token.cs" />
+ <Compile Include="TokenType.cs" />
+ <Compile Include="Utils.cs" />
+ <Compile Include="WordToken.cs" />
+ </ItemGroup>
+ <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
+</Project> \ No newline at end of file
diff --git a/homework/81709/JSCompiler.Core/Lexer.cs b/homework/81709/JSCompiler.Core/Lexer.cs
new file mode 100644
index 0000000..18df998
--- /dev/null
+++ b/homework/81709/JSCompiler.Core/Lexer.cs
@@ -0,0 +1,276 @@
+using JSCompiler.Core;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace JSCompiler.Core
+{
+ public class Lexer
+ {
+ public static readonly string UNCLOSED_MULTILINE_COMMENT = "Unclosed mulitline comment";
+
+ public static readonly string UNCLOSED_STRING = "Unclosed string";
+
+ public static readonly string UNCLOSED_STRING_NEWLINE = "Unclosed string newline";
+
+ public static readonly string UNIDENTIFIED_SYMBOL = "Unidentified symbol";
+
+
+ private Dictionary<string, TokenType> keywords;
+
+ private Dictionary<string, TokenType> otherReserved;
+
+ private IInputStream stream;
+
+ public Lexer(IInputStream stream)
+ {
+ this.stream = stream;
+ this.keywords = ReservedStrings.KeywordsAsMap();
+ this.otherReserved = ReservedStrings.OthersAsMap();
+ }
+
+ public List<Token> Tokenize(Action<int, string> onError)
+ {
+ List<Token> tokens = new List<Token>();
+ do
+ {
+ Token next = NextToken(onError);
+ if(next.Type != TokenType.Skip)
+ {
+ tokens.Add(next);
+ }
+ } while (!stream.IsEndOfStream());
+ tokens.Add(new Token(TokenType.EOF));
+ return tokens;
+ }
+
+ public Token NextToken(Action<int, string> onError)
+ {
+ char cur = stream.Next();
+
+ if (Utils.IsCarriageReturn(cur))
+ return new Token(TokenType.Skip);
+
+ if (Utils.IsWhiteSpace(cur))
+ return new Token(TokenType.Whitespace);
+
+ if (Utils.IsTab(cur))
+ return new Token(TokenType.Tab);
+
+ if (Utils.IsNewLine(cur))
+ return new Token(TokenType.NewLine);
+
+ switch (cur)
+ {
+ case '=':
+ return Match('=') ? Match('=') ? new WordToken("===", TokenType.StrictEqual)
+ : new WordToken("==", TokenType.EqualEqual)
+ : new Token(TokenType.Equal);
+ case '!':
+ return Match('=') ? Match('=') ? new WordToken("!==", TokenType.StrictNotEqual)
+ : new WordToken("!=", TokenType.NotEqual)
+ : new Token(TokenType.Bang);
+ case '>':
+ return Match('=') ? new WordToken(">=", TokenType.GreaterEqual)
+ : Match('>') ? Match('=') ? new WordToken(">>=", TokenType.RightShiftEqual)
+ : new WordToken(">>", TokenType.RightShift)
+ : new Token(TokenType.Greater);
+ case '<':
+ return Match('=') ? new WordToken("<=", TokenType.LessEqual)
+ : Match('<') ? Match('=') ? new WordToken("<<=", TokenType.LeftShiftEqual)
+ : new WordToken("<<", TokenType.LeftShift)
+ : new Token(TokenType.Less);
+ case '+':
+ return Match('+') ? new WordToken("++", TokenType.PlusPlus)
+ : Match('=') ? new WordToken("+=", TokenType.PlusEqual)
+ : new Token(TokenType.Plus);
+ case '-':
+ return Match('-') ? new WordToken("--", TokenType.MinusMinus)
+ : Match('=') ? new WordToken("-=", TokenType.MinusEqual)
+ : new Token(TokenType.Minus);
+ case '*':
+ return Match('=') ? new WordToken("*=", TokenType.StarEqual)
+ : new Token(TokenType.Star);
+ case '/':
+ return Match('/') ? ParseOneLineComment()
+ : Match('*') ? ParseMultilineToken(onError)
+ : Match('=') ? new WordToken("/=", TokenType.DivideEqual)
+ : new Token(TokenType.Division);
+ case '%':
+ return Match('=') ? new WordToken("%=", TokenType.ModuloEqual)
+ : new Token(TokenType.Modulo);
+ case '^':
+ return Match('=') ? new WordToken("^=", TokenType.BitwiseXorEqual)
+ : new Token(TokenType.BitwiseXor);
+ case '|':
+ return Match('=') ? new WordToken("|=", TokenType.BitwiseOrEqual)
+ : Match('|') ? new WordToken("||", TokenType.LogicalOr)
+ : new Token(TokenType.BitwiseOr);
+ case '&':
+ return Match('=') ? new WordToken("&=", TokenType.BitwiseAndEqual)
+ : Match('&') ? new WordToken("&&", TokenType.LogicalAnd)
+ : new Token(TokenType.BitwiseAnd);
+ default:
+ break;
+ }
+
+ if (cur < 127 && Enum.IsDefined(typeof(TokenType), (int)cur))
+ return new Token((TokenType)cur);
+
+ if(Utils.IsStringSingleQuote(cur) || Utils.IsStringDoubleQuote(cur))
+ {
+ return ParseSingleQuoteString(cur, onError);
+ }
+
+ if (char.IsDigit(cur))
+ {
+ return ParseNumber(cur);
+ }
+
+ if (char.IsLetter(cur))
+ {
+ return ParseReservedOrIdentifier(cur);
+ }
+
+ onError(stream.Line, UNIDENTIFIED_SYMBOL);
+ return new WordToken(cur.ToString(), TokenType.Error);
+ }
+
+ private bool Match(char ch)
+ {
+ if(stream.Peek() == ch)
+ {
+ stream.Next();
+ return true;
+ }
+ return false;
+ }
+
+ private Token ParseOneLineComment()
+ {
+ string lexeme = "//";
+ char peek = stream.Peek();
+ while(!Utils.IsNewLine(peek) && !Utils.IsCarriageReturn(peek) && !stream.IsEndOfStream())
+ {
+ lexeme += stream.Next();
+ peek = stream.Peek();
+ }
+ return new WordToken(lexeme, TokenType.Comment);
+ }
+
+ private Token ParseMultilineToken(Action<int, string> onError)
+ {
+ string lexeme = "/*";
+ int lineBeg = stream.Line + 1;
+ while (!stream.IsEndOfStream())
+ {
+ while (stream.Peek() != '*')
+ {
+ if (stream.IsEndOfStream())
+ {
+ onError(lineBeg, UNCLOSED_MULTILINE_COMMENT);
+ return new WordToken(lexeme, TokenType.Comment);
+ }
+
+ lexeme += stream.Next();
+ };
+ lexeme += stream.Next();
+ if (stream.Peek() == '/')
+ {
+ lexeme += stream.Next();
+ return new WordToken(lexeme, TokenType.Comment);
+ }
+ }
+ onError(lineBeg, UNCLOSED_MULTILINE_COMMENT);
+ return new WordToken(lexeme, TokenType.Comment);
+ }
+
+ private Token ParseSingleQuoteString(char quote, Action<int, string> onError)
+ {
+ string lexeme = "" + quote;
+ int startLine = stream.Line + 1;
+ char peek = stream.Peek();
+ while(peek != quote)
+ {
+ if (Utils.IsNewLine(peek) || Utils.IsCarriageReturn(peek))
+ {
+ onError(startLine, UNCLOSED_STRING_NEWLINE);
+ return new WordToken(lexeme, TokenType.Error);
+ }
+
+ if (stream.IsEndOfStream())
+ {
+ onError(startLine, UNCLOSED_STRING);
+ return new WordToken(lexeme, TokenType.Error);
+ }
+
+ lexeme += peek;
+ stream.Next();
+
+ if(peek == '\\')
+ {
+ char next = stream.Peek();
+
+ if (next == quote || next == '\\')
+ {
+ lexeme += next;
+ stream.Next();
+ }
+ else
+ {
+ if (Utils.IsCarriageReturn(next)) //Skip Carriage return
+ {
+ lexeme += stream.Next();
+ next = stream.Peek();
+ }
+ if (Utils.IsNewLine(next))
+ {
+ lexeme += stream.Next();
+ }
+ }
+ }
+
+ peek = stream.Peek();
+ }
+
+ lexeme += stream.Next();
+ return new WordToken(lexeme, TokenType.String);
+ }
+
+ private Token ParseNumber(char cur)
+ {
+ long num = cur - '0';
+ char peek = stream.Peek();
+ while (char.IsDigit(peek) && !stream.IsEndOfStream())
+ {
+ num = num * 10 + (stream.Next() - '0');
+ peek = stream.Peek();
+ }
+ return new NumToken(num);
+ }
+
+ private Token ParseReservedOrIdentifier(char ch)
+ {
+ string lexeme = "" + ch;
+
+ while (char.IsLetterOrDigit(stream.Peek()) && !stream.IsEndOfStream())
+ {
+ lexeme += stream.Next();
+ }
+
+ TokenType reservedType;
+
+ keywords.TryGetValue(lexeme, out reservedType);
+ if(reservedType != 0)
+ return new WordToken(lexeme, reservedType);
+
+ otherReserved.TryGetValue(lexeme, out reservedType);
+ if(reservedType != 0)
+ return new WordToken(lexeme, reservedType);
+
+ return new WordToken(lexeme, TokenType.Identifier);
+ }
+ }
+}
diff --git a/homework/81709/JSCompiler.Core/NumToken.cs b/homework/81709/JSCompiler.Core/NumToken.cs
new file mode 100644
index 0000000..53af034
--- /dev/null
+++ b/homework/81709/JSCompiler.Core/NumToken.cs
@@ -0,0 +1,42 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace JSCompiler.Core
+{
+ public class NumToken : Token, IEquatable<NumToken>
+ {
+ public NumToken(long num) : base(TokenType.Number)
+ {
+ Num = num;
+ }
+
+ public long Num { get; private set; }
+
+ public bool Equals(NumToken other)
+ {
+ return other.Num == Num && base.Equals((Token)other);
+ }
+
+ public override bool Equals(object obj)
+ {
+ var item = obj as NumToken;
+
+ if (item == null) return false;
+
+ return Equals(item);
+ }
+
+ public override int GetHashCode()
+ {
+ return Num.GetHashCode() * 17 + base.GetHashCode();
+ }
+
+ public override string ToString()
+ {
+ return Num.ToString();
+ }
+ }
+}
diff --git a/homework/81709/JSCompiler.Core/Properties/AssemblyInfo.cs b/homework/81709/JSCompiler.Core/Properties/AssemblyInfo.cs
new file mode 100644
index 0000000..ead40a3
--- /dev/null
+++ b/homework/81709/JSCompiler.Core/Properties/AssemblyInfo.cs
@@ -0,0 +1,36 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// General Information about an assembly is controlled through the following
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTitle("JSCompiler.Core")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("JSCompiler.Core")]
+[assembly: AssemblyCopyright("Copyright © 2018")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// Setting ComVisible to false makes the types in this assembly not visible
+// to COM components. If you need to access a type in this assembly from
+// COM, set the ComVisible attribute to true on that type.
+[assembly: ComVisible(false)]
+
+// The following GUID is for the ID of the typelib if this project is exposed to COM
+[assembly: Guid("c8dd3d07-6ae9-444b-8ba0-22dc633a9432")]
+
+// Version information for an assembly consists of the following four values:
+//
+// Major Version
+// Minor Version
+// Build Number
+// Revision
+//
+// You can specify all the values or you can default the Build and Revision Numbers
+// by using the '*' as shown below:
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]
diff --git a/homework/81709/JSCompiler.Core/ReservedStrings.cs b/homework/81709/JSCompiler.Core/ReservedStrings.cs
new file mode 100644
index 0000000..261e444
--- /dev/null
+++ b/homework/81709/JSCompiler.Core/ReservedStrings.cs
@@ -0,0 +1,82 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace JSCompiler.Core
+{
+ public static class ReservedStrings
+ {
+ public static Dictionary<string, TokenType> KeywordsAsMap()
+ {
+ return new Dictionary<string, TokenType>
+ {
+ {"break", TokenType.Break },
+ {"case", TokenType.Case },
+ {"catch", TokenType.Catch },
+ {"class", TokenType.Class },
+ {"const", TokenType.Const },
+ {"continue", TokenType.Continue },
+ {"debugger", TokenType.Debugger },
+ {"default", TokenType.Default },
+ {"delete", TokenType.Delete },
+ {"do" , TokenType.Do },
+ {"else", TokenType.Else },
+ {"export", TokenType.Export },
+ {"extends", TokenType.Extends },
+ {"finally", TokenType.Finally },
+ {"for", TokenType.For },
+ {"function", TokenType.Function },
+ {"if", TokenType.If },
+ {"import", TokenType.Import },
+ {"in", TokenType.In },
+ {"instance", TokenType.Instanceof },
+ {"new", TokenType.New },
+ {"return", TokenType.Return },
+ {"super", TokenType.Super },
+ {"switch", TokenType.Switch },
+ {"this", TokenType.This },
+ {"throw", TokenType.Throw },
+ {"try", TokenType.Try },
+ {"typeof", TokenType.Typeof },
+ {"var", TokenType.Var },
+ {"let", TokenType.Let },
+ {"void", TokenType.Void },
+ {"while", TokenType.While },
+ {"with", TokenType.With },
+ {"yield", TokenType.Yield },
+ {"null", TokenType.Null },
+ {"true", TokenType.True },
+ { "false", TokenType.False }
+ };
+ }
+
+ public static Dictionary<string, TokenType> OthersAsMap()
+ {
+ return new Dictionary<string, TokenType>
+ {
+ {"Array", TokenType.Array },
+ {"Date", TokenType.Date },
+ {"eval", TokenType.Eval },
+ {"hasOwnProperty", TokenType.HasOwnProperty },
+ {"Infinity", TokenType.Infinity },
+ {"isFinite", TokenType.IsFinite },
+ {"isNaN", TokenType.IsNaN },
+ {"isPrototypeOf", TokenType.IsPrototypeOf },
+ {"length", TokenType.Length },
+ {"Math" , TokenType.Math },
+ {"NaN", TokenType.NaN },
+ {"name", TokenType.Name },
+ {"String", TokenType.StringObj },
+ {"Number", TokenType.NumberObj },
+ {"toString", TokenType.ToString },
+ {"Object", TokenType.Object },
+ {"undefined", TokenType.Undefined },
+ {"prototype", TokenType.Prototype },
+ {"valueOf", TokenType.ValueOf }
+ };
+ }
+
+ }
+}
diff --git a/homework/81709/JSCompiler.Core/Token.cs b/homework/81709/JSCompiler.Core/Token.cs
new file mode 100644
index 0000000..efa2dd8
--- /dev/null
+++ b/homework/81709/JSCompiler.Core/Token.cs
@@ -0,0 +1,48 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace JSCompiler.Core
+{
+ public class Token : IEquatable<Token>
+ {
+ public Token(TokenType type)
+ {
+ Type = type;
+ }
+
+ public TokenType Type { get; private set; }
+
+ public override bool Equals(object obj)
+ {
+ var item = obj as Token;
+
+ if(item == null)
+ {
+ return false;
+ }
+
+ return Equals(item);
+ }
+
+ public bool Equals(Token other)
+ {
+ return Type == other.Type;
+ }
+
+ public override int GetHashCode()
+ {
+ return this.Type.GetHashCode();
+ }
+
+ public override string ToString()
+ {
+ return ((char)Type).ToString();
+ }
+ }
+
+
+
+}
diff --git a/homework/81709/JSCompiler.Core/TokenType.cs b/homework/81709/JSCompiler.Core/TokenType.cs
new file mode 100644
index 0000000..a65f7a2
--- /dev/null
+++ b/homework/81709/JSCompiler.Core/TokenType.cs
@@ -0,0 +1,134 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace JSCompiler.Core
+{
+ public enum TokenType
+ {
+ // Single-character token types mapped to ASCII codes
+ LeftParen = 40, // (
+ RightParen = 41, // )
+ LeftBrace = 123, // {
+ RightBrace = 125, // }
+ Comma = 44, // ,
+ Dot = 46, // .
+ Minus = 45, // -
+ Plus = 43, // +
+ Semicolon = 59, // ;
+ Star = 42, // *
+ Division = 47, // /
+ Modulo = 37, // %
+ BitwiseNot = 126, // ~
+ BitwiseAnd = 38, // &
+ BitwiseXor = 94, // ^
+ BitwiseOr = 124, // |
+ QuestionMark = 63, // ?
+ Colon = 58, // :
+ LeftSquareBracket = 91, // [
+ RightSquareBracket = 93, // ]
+ Bang = 33, // !
+ Equal = 61, // =
+ Greater = 62, // >
+ Less = 60, // <
+ Backslash = 92, // \
+
+ Invalid = 127,
+ NewLine,
+ Tab,
+ Whitespace,
+ Comment,
+ EOF,
+ Skip,
+
+ NotEqual, // !=
+ EqualEqual, // ==
+ StrictEqual, // ===
+ StrictNotEqual, // !==
+ GreaterEqual, // >=
+ LessEqual, // <=
+ MinusMinus, // --
+ PlusPlus, // ++
+ LeftShift, // <<
+ RightShift, // >>
+ LogicalAnd, // &&
+ LogicalOr, // ||
+ StarEqual, // *=
+ DivideEqual, // /=
+ ModuloEqual, // %=
+ PlusEqual, // +=
+ MinusEqual, // -=
+ LeftShiftEqual, // <<=
+ RightShiftEqual, // >>=
+ BitwiseAndEqual, // &=
+ BitwiseXorEqual, // ^=
+ BitwiseOrEqual, // |=
+
+ Identifier,
+ String,
+ Number,
+ Error,
+
+ // Keywords. ECMAScript 2015
+ Break,
+ Case,
+ Catch,
+ Class,
+ Const,
+ Continue,
+ Debugger,
+ Default,
+ Delete,
+ Do,
+ Else,
+ Export,
+ Extends,
+ Finally,
+ For,
+ Function,
+ If,
+ Import,
+ In,
+ Instanceof,
+ New,
+ Return,
+ Super,
+ Switch,
+ This,
+ Throw,
+ Try,
+ Typeof,
+ Var,
+ Let,
+ Void,
+ While,
+ With,
+ Yield,
+ Null,
+ True,
+ False,
+
+ //Objects and other
+ Array,
+ Date,
+ Eval,
+ HasOwnProperty,
+ Infinity,
+ IsFinite,
+ IsNaN,
+ IsPrototypeOf,
+ Length,
+ Math,
+ NaN,
+ Name,
+ NumberObj,
+ Object,
+ Prototype,
+ StringObj,
+ ToString,
+ Undefined,
+ ValueOf
+ }
+}
diff --git a/homework/81709/JSCompiler.Core/Utils.cs b/homework/81709/JSCompiler.Core/Utils.cs
new file mode 100644
index 0000000..17210bc
--- /dev/null
+++ b/homework/81709/JSCompiler.Core/Utils.cs
@@ -0,0 +1,41 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace JSCompiler.Core
+{
+ public static class Utils
+ {
+ public static bool IsWhiteSpace(char ch)
+ {
+ return ch == ' ';
+ }
+
+ public static bool IsCarriageReturn(char ch)
+ {
+ return ch == '\r';
+ }
+
+ public static bool IsNewLine(char ch)
+ {
+ return ch == '\n';
+ }
+
+ public static bool IsTab(char ch)
+ {
+ return ch == '\t';
+ }
+
+ public static bool IsStringSingleQuote(char ch)
+ {
+ return ch == '\'';
+ }
+
+ public static bool IsStringDoubleQuote(char ch)
+ {
+ return ch == '"';
+ }
+ }
+}
diff --git a/homework/81709/JSCompiler.Core/WordToken.cs b/homework/81709/JSCompiler.Core/WordToken.cs
new file mode 100644
index 0000000..cee419e
--- /dev/null
+++ b/homework/81709/JSCompiler.Core/WordToken.cs
@@ -0,0 +1,45 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace JSCompiler.Core
+{
+ public class WordToken : Token, IEquatable<WordToken>
+ {
+ public WordToken(string lexeme, TokenType type) : base(type)
+ {
+ Lexeme = lexeme;
+ }
+
+ public string Lexeme { get; private set; }
+
+ public override bool Equals(object obj)
+ {
+ var item = obj as WordToken;
+
+ if(item == null)
+ {
+ return false;
+ }
+
+ return Equals(item);
+ }
+
+ public bool Equals(WordToken token)
+ {
+ return base.Equals((Token) token) && string.Equals(Lexeme, token.Lexeme);
+ }
+
+ public override int GetHashCode()
+ {
+ return Lexeme.GetHashCode() * 17 + base.GetHashCode();
+ }
+
+ public override string ToString()
+ {
+ return Lexeme;
+ }
+ }
+}
diff --git a/homework/81709/JSCompiler.SyntaxHighlighter/App.config b/homework/81709/JSCompiler.SyntaxHighlighter/App.config
new file mode 100644
index 0000000..62c8f44
--- /dev/null
+++ b/homework/81709/JSCompiler.SyntaxHighlighter/App.config
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<configuration>
+ <startup>
+ <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.7.1"/>
+ </startup>
+</configuration>
diff --git a/homework/81709/JSCompiler.SyntaxHighlighter/JSCompiler.SyntaxHighlighter.csproj b/homework/81709/JSCompiler.SyntaxHighlighter/JSCompiler.SyntaxHighlighter.csproj
new file mode 100644
index 0000000..cb321ea
--- /dev/null
+++ b/homework/81709/JSCompiler.SyntaxHighlighter/JSCompiler.SyntaxHighlighter.csproj
@@ -0,0 +1,62 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
+ <PropertyGroup>
+ <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+ <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+ <ProjectGuid>{D555ADB4-0161-4DF9-8098-1A72C03C1576}</ProjectGuid>
+ <OutputType>Exe</OutputType>
+ <RootNamespace>JSCompiler.SyntaxHighlighter</RootNamespace>
+ <AssemblyName>JSCompiler.SyntaxHighlighter</AssemblyName>
+ <TargetFrameworkVersion>v4.7.1</TargetFrameworkVersion>
+ <FileAlignment>512</FileAlignment>
+ <AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
+ <TargetFrameworkProfile />
+ </PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
+ <PlatformTarget>AnyCPU</PlatformTarget>
+ <DebugSymbols>true</DebugSymbols>
+ <DebugType>full</DebugType>
+ <Optimize>false</Optimize>
+ <OutputPath>bin\Debug\</OutputPath>
+ <DefineConstants>DEBUG;TRACE</DefineConstants>
+ <ErrorReport>prompt</ErrorReport>
+ <WarningLevel>4</WarningLevel>
+ </PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
+ <PlatformTarget>AnyCPU</PlatformTarget>
+ <DebugType>pdbonly</DebugType>
+ <Optimize>true</Optimize>
+ <OutputPath>bin\Release\</OutputPath>
+ <DefineConstants>TRACE</DefineConstants>
+ <ErrorReport>prompt</ErrorReport>
+ <WarningLevel>4</WarningLevel>
+ </PropertyGroup>
+ <ItemGroup>
+ <Reference Include="System" />
+ <Reference Include="System.Core" />
+ <Reference Include="System.Xml.Linq" />
+ <Reference Include="System.Data.DataSetExtensions" />
+ <Reference Include="Microsoft.CSharp" />
+ <Reference Include="System.Data" />
+ <Reference Include="System.Net.Http" />
+ <Reference Include="System.Xml" />
+ </ItemGroup>
+ <ItemGroup>
+ <Compile Include="Program.cs" />
+ <Compile Include="Properties\AssemblyInfo.cs" />
+ <Compile Include="SyntaxHighlighter.cs" />
+ </ItemGroup>
+ <ItemGroup>
+ <None Include="App.config">
+ <SubType>Designer</SubType>
+ </None>
+ </ItemGroup>
+ <ItemGroup>
+ <ProjectReference Include="..\JSCompiler.Core\JSCompiler.Core.csproj">
+ <Project>{c8dd3d07-6ae9-444b-8ba0-22dc633a9432}</Project>
+ <Name>JSCompiler.Core</Name>
+ </ProjectReference>
+ </ItemGroup>
+ <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
+</Project> \ No newline at end of file
diff --git a/homework/81709/JSCompiler.SyntaxHighlighter/Program.cs b/homework/81709/JSCompiler.SyntaxHighlighter/Program.cs
new file mode 100644
index 0000000..fd0d539
--- /dev/null
+++ b/homework/81709/JSCompiler.SyntaxHighlighter/Program.cs
@@ -0,0 +1,36 @@
+using JSCompiler.Core;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace JSCompiler.SyntaxHighlighter
+{
+ class Program
+ {
+ static int Main(string[] args)
+ {
+ if(args.Length < 2)
+ {
+ Console.WriteLine("Input and output destinations not specified!");
+ return 1;
+ }
+
+ try
+ {
+ BaseInputStream stream = new BaseInputStream(args[0]);
+ Lexer lex = new Lexer(stream);
+ List<Token> tokens = lex.Tokenize((line, cause) => Console.Out.WriteLine($"On line {line} error: {cause}"));
+ SyntaxHighlighter.GenerateHtml(tokens, args[1]);
+ }
+ catch(Exception e)
+ {
+ Console.WriteLine(e.Message);
+ return 1;
+ }
+
+ return 0;
+ }
+ }
+}
diff --git a/homework/81709/JSCompiler.SyntaxHighlighter/Properties/AssemblyInfo.cs b/homework/81709/JSCompiler.SyntaxHighlighter/Properties/AssemblyInfo.cs
new file mode 100644
index 0000000..6d5d657
--- /dev/null
+++ b/homework/81709/JSCompiler.SyntaxHighlighter/Properties/AssemblyInfo.cs
@@ -0,0 +1,36 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// General Information about an assembly is controlled through the following
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTitle("JSCompiler.SyntaxHighlighter")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("JSCompiler.SyntaxHighlighter")]
+[assembly: AssemblyCopyright("Copyright © 2018")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// Setting ComVisible to false makes the types in this assembly not visible
+// to COM components. If you need to access a type in this assembly from
+// COM, set the ComVisible attribute to true on that type.
+[assembly: ComVisible(false)]
+
+// The following GUID is for the ID of the typelib if this project is exposed to COM
+[assembly: Guid("d555adb4-0161-4df9-8098-1a72c03c1576")]
+
+// Version information for an assembly consists of the following four values:
+//
+// Major Version
+// Minor Version
+// Build Number
+// Revision
+//
+// You can specify all the values or you can default the Build and Revision Numbers
+// by using the '*' as shown below:
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]
diff --git a/homework/81709/JSCompiler.SyntaxHighlighter/SyntaxHighlighter.cs b/homework/81709/JSCompiler.SyntaxHighlighter/SyntaxHighlighter.cs
new file mode 100644
index 0000000..fd8572f
--- /dev/null
+++ b/homework/81709/JSCompiler.SyntaxHighlighter/SyntaxHighlighter.cs
@@ -0,0 +1,187 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.IO;
+using JSCompiler.Core;
+
+namespace JSCompiler.SyntaxHighlighter
+{
+ public static class SyntaxHighlighter
+ {
+ private static readonly string HTML =
+ @"<!DOCTYPE html>
+<html>
+<head>
+{0}
+<title> {1} </title>
+</head>
+<body> {2} </body></html>";
+
+ private static readonly string DEFAULT_CSS =
+ @"<style>
+body {
+ font-size: 20px;
+ font-family: ""Verdana"";
+}
+.number {
+ color: Orange;
+}
+.string {
+ color: Gray;
+ white-space: pre-wrap;
+}
+.keyword {
+ color: Blue;
+}
+.identifier {
+ color: Black;
+}
+.operator {
+ color: DarkBlue;
+}
+.comment {
+ color: Green;
+ font-style: italic;
+ white-space: pre-wrap;
+}
+
+.reserved {
+ color: MediumVioletRed;
+}
+
+.undefined{
+ color: Black;
+}
+
+.default {
+ color: DarkBlue;
+}
+</style>";
+
+ private static IEnumerable<TokenType> KEYWORDS = ReservedStrings.KeywordsAsMap().Values;
+
+ private static IEnumerable<TokenType> OTHER_RESERVED = ReservedStrings.OthersAsMap().Values;
+
+ private static IEnumerable<TokenType> ONE_SYMBOL_OPERATORS = new List<TokenType>
+ {
+ TokenType.Minus, // -
+ TokenType.Plus, // +
+ TokenType.Star, // *
+ TokenType.Division, // /
+ TokenType.Modulo, // %
+ TokenType.BitwiseNot, // ~
+ TokenType.BitwiseAnd, // &
+ TokenType.BitwiseXor, // ^
+ TokenType.BitwiseOr, // |
+ TokenType.QuestionMark, // ?
+ TokenType.Bang, // !
+ TokenType.Equal, // =
+ TokenType.Greater, // >
+ TokenType.Less, // <
+ TokenType.Backslash, // \
+ };
+
+ private static IEnumerable<TokenType> MULT_SYMBOL_OPERATORS = new List<TokenType>
+ {
+ TokenType.NotEqual, // !=
+ TokenType.EqualEqual, // ==
+ TokenType.StrictEqual, // ===
+ TokenType.StrictNotEqual, // !==
+ TokenType.GreaterEqual, // >=
+ TokenType.LessEqual, // <=
+ TokenType.MinusMinus, // --
+ TokenType.PlusPlus, // ++
+ TokenType.LeftShift, // <<
+ TokenType.RightShift, // >>
+ TokenType.LogicalAnd, // &&
+ TokenType.LogicalOr, // ||
+ TokenType.StarEqual, // *=
+ TokenType.DivideEqual, // /=
+ TokenType.ModuloEqual, // %=
+ TokenType.PlusEqual, // +=
+ TokenType.MinusEqual, // -=
+ TokenType.LeftShiftEqual, // <<=
+ TokenType.RightShiftEqual, // >>=
+ TokenType.BitwiseAndEqual, // &=
+ TokenType.BitwiseXorEqual, // ^=
+ TokenType.BitwiseOrEqual, // |=
+ };
+
+ public static void GenerateHtml(IEnumerable<Token> tokens, string fileName, string css = null)
+ {
+ if (css == null) css = DEFAULT_CSS;
+
+ string code = "";
+ IEnumerator<Token> tokenEnum = tokens.GetEnumerator();
+ if (!tokenEnum.MoveNext()) return;
+ Token cur;
+ do
+ {
+ cur = tokenEnum.Current;
+ TokenType type = cur.Type;
+
+ switch (type)
+ {
+ case TokenType.Whitespace:
+ code += "&nbsp";
+ break;
+ case TokenType.Tab:
+ code += "&nbsp" + "&nbsp" + "&nbsp" + "&nbsp";
+ break;
+ case TokenType.NewLine:
+ code += "<br>";
+ break;
+ case TokenType.Number:
+ code += "<span class=\"number\">" + ((NumToken)cur).Num + "</span>";
+ break;
+ case TokenType.String:
+ code += "<span class=\"string\">" + ((WordToken)cur).Lexeme + "</span>";
+ break;
+ case TokenType.Identifier:
+ code += "<span class=\"identifier\">" + ((WordToken)cur).Lexeme + "</span>";
+ break;
+ case TokenType.Comment:
+ code += "<span class=\"comment\">" + ((WordToken)cur).Lexeme + "</span>";
+ break;
+ case TokenType.Error:
+ code += "<span class=\"undefined\">" + ((WordToken)cur).Lexeme + "</span>";
+ break;
+
+ default:
+ if (KEYWORDS.Contains(type))
+ {
+ code += "<span class=\"keyword\">" + ((WordToken)cur).Lexeme + "</span>";
+ }
+ else if(OTHER_RESERVED.Contains(type))
+ {
+ code += "<span class=\"reserved\">" + ((WordToken)cur).Lexeme + "</span>";
+ }
+ else if (ONE_SYMBOL_OPERATORS.Contains(type))
+ {
+ code += "<span class=\"operator\">" + cur.ToString() + "</span>";
+ }
+ else if (MULT_SYMBOL_OPERATORS.Contains(type))
+ {
+ code += "<span class=\"operator\">" + ((WordToken)cur).Lexeme + "</span>";
+ }
+ else
+ {
+ code += "<span class=\"default\">" + cur.ToString() + "</span>";
+ break;
+ }
+ break;
+ }
+ }
+ while (tokenEnum.MoveNext() && tokenEnum.Current.Type != TokenType.EOF);
+
+ string shouldWrite = string.Format(HTML, css, fileName, code);
+
+ using (StreamWriter writer = new StreamWriter(fileName))
+ {
+ writer.Write(shouldWrite);
+ }
+ }
+ }
+}
diff --git a/homework/81709/JSCompiler.SyntaxHighlighter/test/test.js b/homework/81709/JSCompiler.SyntaxHighlighter/test/test.js
new file mode 100644
index 0000000..537e59f
--- /dev/null
+++ b/homework/81709/JSCompiler.SyntaxHighlighter/test/test.js
@@ -0,0 +1,23 @@
+/*
+** This is a test function to demonstrate syntax highlighting!
+*/
+function myFunction() {
+ var res = "";
+ res = res +isNaN(123) + ": 123";
+ res = res + isNaN(-1.23) + ": -1.23";
+ res = res + isNaN(5-2) + ": 5-2";
+ res = res + isNaN(0) + ": 0";
+ res = res + isNaN('123') + ": 123
+ ";
+ //oops!
+ res = res + isNaN(true) + ": true";
+ res = res + isNaN(undefined) + ": undefined \
+ nextString \
+ ";
+ res = res + isNaN('NaN') + ": NaN 'Hell no'";
+ res = res + isNaN(NaN) + ': NaN \\n \';
+ res = res + isNaN(0 / 0) + ": 0 / 0 \ ";
+
+ document.getElementById("demo").innerHTML = res;
+}
+//Cool, it works! \ No newline at end of file
diff --git a/homework/81709/JSCompiler.SyntaxHighlighter/test/testout.html b/homework/81709/JSCompiler.SyntaxHighlighter/test/testout.html
new file mode 100644
index 0000000..d1182c2
--- /dev/null
+++ b/homework/81709/JSCompiler.SyntaxHighlighter/test/testout.html
@@ -0,0 +1,49 @@
+<!DOCTYPE html>
+<html>
+<head>
+<style>
+body {
+ font-size: 20px;
+ font-family: "Verdana";
+}
+.number {
+ color: Orange;
+}
+.string {
+ color: Gray;
+ white-space: pre-wrap;
+}
+.keyword {
+ color: Blue;
+}
+.identifier {
+ color: Black;
+}
+.operator {
+ color: DarkBlue;
+}
+.comment {
+ color: Green;
+ font-style: italic;
+ white-space: pre-wrap;
+}
+
+.reserved {
+ color: MediumVioletRed;
+}
+
+.undefined{
+ color: Black;
+}
+
+.default {
+ color: DarkBlue;
+}
+</style>
+<title> ..\..\test\testout.html </title>
+</head>
+<body> <span class="comment">/*
+** This is a test function to demonstrate syntax highlighting!
+*/</span><br><span class="keyword">function</span>&nbsp<span class="identifier">myFunction</span><span class="default">(</span><span class="default">)</span>&nbsp<span class="default">{</span><br>&nbsp&nbsp&nbsp&nbsp<span class="keyword">var</span>&nbsp<span class="identifier">res</span>&nbsp<span class="operator">=</span>&nbsp<span class="string">""</span><span class="default">;</span><br>&nbsp&nbsp&nbsp&nbsp<span class="identifier">res</span>&nbsp<span class="operator">=</span>&nbsp<span class="identifier">res</span>&nbsp<span class="operator">+</span><span class="reserved">isNaN</span><span class="default">(</span><span class="number">123</span><span class="default">)</span>&nbsp<span class="operator">+</span>&nbsp<span class="string">": 123"</span><span class="default">;</span><br>&nbsp&nbsp&nbsp&nbsp<span class="identifier">res</span>&nbsp<span class="operator">=</span>&nbsp<span class="identifier">res</span>&nbsp<span class="operator">+</span>&nbsp<span class="reserved">isNaN</span><span class="default">(</span><span class="operator">-</span><span class="number">1</span><span class="default">.</span><span class="number">23</span><span class="default">)</span>&nbsp<span class="operator">+</span>&nbsp<span class="string">": -1.23"</span><span class="default">;</span><br>&nbsp&nbsp&nbsp&nbsp<span class="identifier">res</span>&nbsp<span class="operator">=</span>&nbsp<span class="identifier">res</span>&nbsp<span class="operator">+</span>&nbsp<span class="reserved">isNaN</span><span class="default">(</span><span class="number">5</span><span class="operator">-</span><span class="number">2</span><span class="default">)</span>&nbsp<span class="operator">+</span>&nbsp<span class="string">": 5-2"</span><span class="default">;</span><br>&nbsp&nbsp&nbsp&nbsp<span class="identifier">res</span>&nbsp<span class="operator">=</span>&nbsp<span class="identifier">res</span>&nbsp<span class="operator">+</span>&nbsp<span class="reserved">isNaN</span><span class="default">(</span><span class="number">0</span><span class="default">)</span>&nbsp<span class="operator">+</span>&nbsp<span class="string">": 0"</span><span class="default">;</span><br>&nbsp&nbsp&nbsp&nbsp<span class="identifier">res</span>&nbsp<span class="operator">=</span>&nbsp<span class="identifier">res</span>&nbsp<span class="operator">+</span>&nbsp<span class="reserved">isNaN</span><span class="default">(</span><span class="string">'123'</span><span class="default">)</span>&nbsp<span class="operator">+</span>&nbsp<span class="undefined">": 123</span><br>&nbsp&nbsp&nbsp&nbsp<span class="undefined">"; </span><br>&nbsp&nbsp&nbsp&nbsp<span class="comment">//oops!</span><br>&nbsp&nbsp&nbsp&nbsp<span class="identifier">res</span>&nbsp<span class="operator">=</span>&nbsp<span class="identifier">res</span>&nbsp<span class="operator">+</span>&nbsp<span class="reserved">isNaN</span><span class="default">(</span><span class="keyword">true</span><span class="default">)</span>&nbsp<span class="operator">+</span>&nbsp<span class="string">": true"</span><span class="default">;</span><br>&nbsp&nbsp&nbsp&nbsp<span class="identifier">res</span>&nbsp<span class="operator">=</span>&nbsp<span class="identifier">res</span>&nbsp<span class="operator">+</span>&nbsp<span class="reserved">isNaN</span><span class="default">(</span><span class="reserved">undefined</span><span class="default">)</span>&nbsp<span class="operator">+</span>&nbsp<span class="string">": undefined \
+ nextString \
+ "</span><span class="default">;</span><br>&nbsp&nbsp&nbsp&nbsp<span class="identifier">res</span>&nbsp<span class="operator">=</span>&nbsp<span class="identifier">res</span>&nbsp<span class="operator">+</span>&nbsp<span class="reserved">isNaN</span><span class="default">(</span><span class="string">'NaN'</span><span class="default">)</span>&nbsp<span class="operator">+</span>&nbsp<span class="string">": NaN 'Hell no'"</span><span class="default">;</span><br>&nbsp&nbsp&nbsp&nbsp<span class="identifier">res</span>&nbsp<span class="operator">=</span>&nbsp<span class="identifier">res</span>&nbsp<span class="operator">+</span>&nbsp<span class="reserved">isNaN</span><span class="default">(</span><span class="reserved">NaN</span><span class="default">)</span>&nbsp<span class="operator">+</span>&nbsp<span class="undefined">': NaN \\n \';</span><br>&nbsp&nbsp&nbsp&nbsp<span class="identifier">res</span>&nbsp<span class="operator">=</span>&nbsp<span class="identifier">res</span>&nbsp<span class="operator">+</span>&nbsp<span class="reserved">isNaN</span><span class="default">(</span><span class="number">0</span>&nbsp<span class="operator">/</span>&nbsp<span class="number">0</span><span class="default">)</span>&nbsp<span class="operator">+</span>&nbsp<span class="string">": 0 / 0 \ "</span><span class="default">;</span><br><br>&nbsp&nbsp&nbsp&nbsp<span class="identifier">document</span><span class="default">.</span><span class="identifier">getElementById</span><span class="default">(</span><span class="string">"demo"</span><span class="default">)</span><span class="default">.</span><span class="identifier">innerHTML</span>&nbsp<span class="operator">=</span>&nbsp<span class="identifier">res</span><span class="default">;</span><br><span class="default">}</span><br><span class="comment">//Cool, it works!</span> </body></html> \ No newline at end of file
diff --git a/homework/81709/JSCompiler.sln b/homework/81709/JSCompiler.sln
new file mode 100644
index 0000000..ab09e37
--- /dev/null
+++ b/homework/81709/JSCompiler.sln
@@ -0,0 +1,42 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio 15
+VisualStudioVersion = 15.0.27130.2036
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "JSCompiler.Core", "JSCompiler.Core\JSCompiler.Core.csproj", "{C8DD3D07-6AE9-444B-8BA0-22DC633A9432}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "JSCompiler.SyntaxHighlighter", "JSCompiler.SyntaxHighlighter\JSCompiler.SyntaxHighlighter.csproj", "{D555ADB4-0161-4DF9-8098-1A72C03C1576}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{7A1EFAF1-7755-47BF-ACC3-E17F3C456CEC}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "JSCompiler.Core.Tests", "JSCompiler.Core.Tests\JSCompiler.Core.Tests.csproj", "{F2211B16-C27C-40D6-A856-8A6B5A3787EF}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {C8DD3D07-6AE9-444B-8BA0-22DC633A9432}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {C8DD3D07-6AE9-444B-8BA0-22DC633A9432}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {C8DD3D07-6AE9-444B-8BA0-22DC633A9432}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {C8DD3D07-6AE9-444B-8BA0-22DC633A9432}.Release|Any CPU.Build.0 = Release|Any CPU
+ {D555ADB4-0161-4DF9-8098-1A72C03C1576}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {D555ADB4-0161-4DF9-8098-1A72C03C1576}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {D555ADB4-0161-4DF9-8098-1A72C03C1576}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {D555ADB4-0161-4DF9-8098-1A72C03C1576}.Release|Any CPU.Build.0 = Release|Any CPU
+ {F2211B16-C27C-40D6-A856-8A6B5A3787EF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {F2211B16-C27C-40D6-A856-8A6B5A3787EF}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {F2211B16-C27C-40D6-A856-8A6B5A3787EF}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {F2211B16-C27C-40D6-A856-8A6B5A3787EF}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(NestedProjects) = preSolution
+ {F2211B16-C27C-40D6-A856-8A6B5A3787EF} = {7A1EFAF1-7755-47BF-ACC3-E17F3C456CEC}
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {4D495C27-7CDB-4B11-9A8B-0919861303BC}
+ EndGlobalSection
+EndGlobal
diff --git a/homework/81709/README.md b/homework/81709/README.md
new file mode 100644
index 0000000..2d3f4b1
--- /dev/null
+++ b/homework/81709/README.md
@@ -0,0 +1,5 @@
+# Syntax Highlighter
+
+Run JSCompiler.SyntaxHighlighter with two command line arguments - first is the path to the input file you want to highlight and the second is the path to the html file you want to generate.
+
+> Example arguments: ..\\..\\test\\test.js ..\\..\\test\\testout.html \ No newline at end of file