Trying out Prism.js

Lucca RodriguesMay 30, 2021

web design meta

I’ve just added a new library to my website, Prism.js, which adds syntax highlighting and overall better code snippets.

Since my posts often place a heavy emphasis on coding, I figured that having quality code snippets with adequate syntax highlighting (among other things) will add a lot of value to my programming-related posts.

And from the looks of it, Prism is working pretty well. Just to demonstrate, here are a couple of example code snippets.

The first example code snippet is a small part of the main bot.py file from my RedstoneBot project, written in Python.

# module imports
import asyncio
import aiohttp
import discord
from discord.ext import commands

# function imports from files
from login import login
from format_help import format_help
from get_status import get_status
from get_times import get_times
from activate import activate
from deactivate import deactivate
from reactivate import reactivate
from register import register
from leave_queue import leave_queue
from unregister import unregister
from get_list import get_list

# member role functions
from role_functions import (
    check_user,
    check_admin,
)

# database functions
from db_functions import (
    link,
    guild_in_db,
    IP_in_db,
    get_serverID,
    get_looping,
    update_looping,
    deleteEntry,
)

# credentials
from credentials import (
    username,
    password,
    token,
)

# -----------------------------------------------------------------
# Discord setup
# -----------------------------------------------------------------



# command prefix
client = commands.Bot(command_prefix = '!redstone ')

# Redstone dust reddish color for Rich Embeds
redstoneRed = discord.Colour.from_rgb(221,55,55)

# removing default Help command
client.remove_command('help')

# bot startup
@client.event
async def on_ready():
    # retrieving aiohttp ClientSession
    global session_list
    session = session_list[0]

    # logging in to ploudos.com
    login_status_code = await login(username, password, session)
    print('\nLogin status code: ' + str(login_status_code))

    print('Bot is ready to roll!\n')



# -----------------------------------------------------------------
# Discord commands
# -----------------------------------------------------------------


# help command - shows useful help page w/ commands + other things
@client.command()
async def help(ctx):

    title, content = format_help()
    # format and send rich embed
    page=discord.Embed(
        title=title,
        description=content,
        colour=redstoneRed
    )
    await ctx.send(embed=page)





# ping command - replies with "Pong!" + connection latency in miliseconds
@client.command()
async def ping(ctx):
    await ctx.send(f'Pong! :ping_pong: Connection latency is {round(client.latency * 1000)}ms')

For the second example, I’ll try a code snippet in C++. This is a Reverse Polish Notation parser and evaluator that I wrote a couple of months ago with the help of my friend Eduardo Quirino.

/*
    -- RPN Parser --

    Parses and evaluates reverse polish
    notation expressions.

*/


#include <iostream> // input/output
#include <typeinfo>
#include <stack>    // stack data structure
#include <queue>
#include <math.h>   // quick maths
#include <string>   // guess what this does?
#include <sstream>  // stringstreams

#include <vector>

using namespace std;


/*
numberStack.push(5); -> puts element into stack
numberStack.pop();   -> removes top element
numberStack.top();   -> gets top element

numberStack.size()   -> number of elements
numberStack.empty()  -> [BOOL] whether it is emtpy
*/




// Returns whether string is a number
bool isNumber(string s) {
  // looping over chars in string

  // checking for empty input
  if (s.size() == 0) return false;

  for (int i = 0; i < s.size(); i++) {
    char letter = s[i];

    // check if letter isn't digit or decimal point
    if (letter < '0' || letter > '9') {
      // checks minus case
      if ((letter == '-' && i == 0) && s.size() > 1)
        continue;
      // checks decimal point case
      if (letter == '.') continue;

      return false;
    }
  }
  // found an actual number
  return true;
}





// Splits expression into queue
queue<string> string2queue(string expression) {
  queue<string> token_queue;
  /*
  token_queue.push(3)   -> enqueues element
  token_queue.pop()     -> dequeues element
  token_queue.front()   -> gets first element
  */

  // empty string token
  string token = "";

  // iterating through expression
  for (int i = 0; i < expression.size(); i++) {
    char letter = expression[i];

    // adding letter to token
    if (letter != ' ') token += letter;
    else {      // found a whitespace char
      if (!token.empty()) { // protects against multiple whitespaces
        // ready to push token to queue
        token_queue.push(token);
        //cout << "This is token, right here: " << token << endl;
        token="";
      } // else, just another whitespace, keep going
    }
  }
  // adds token at the end of the expression (edge case)
  if ( !token.empty() ) token_queue.push(token);

  return token_queue;
}




// print elements in a queue
void print_queue(queue<string> q){
  int i = 0;
  while (!q.empty())
  {
    cout << i << "-th element: " << q.front() << endl;
    q.pop();
    i++;
  }
  cout << endl;
}




// evaluate RPN expression
double solve(string expression) {

  // generating queue of tokens
  queue<string> token_queue = string2queue(expression);

  /*
  // print copy of token_queue
  queue<string> q_copy = token_queue;
  print_queue(q_copy);
  */


  // big 'ol stack of numbers
  stack<double> numberStack;

  while (!token_queue.empty()) {
    // getting the token at the front of the queue
    string token = token_queue.front();
    token_queue.pop();

    // checking if token is a number
    if (isNumber(token)) {
      //cout << token << " is number\n";

      // Converting string token to double
      double numToken = stod(token);
      // pushing number to stack
      numberStack.push(numToken);
    } else {
      // found an operator
      if (numberStack.size() < 2) {
        cout << "SYNTAX ERROR - Not enough numbers in stack\n";
        exit(1);
      }

      // getting and popping the RH operand
      //cout << numberStack.top() << " is a: " << typeid(numberStack.top()).name() << endl;
      double b = numberStack.top() + 0.0;
      numberStack.pop();
      //cout << b << " is: " << typeid(b).name() << endl;
      // getting and popping the LH operand
      //cout << numberStack.top() << " is a: " << typeid(numberStack.top()).name() << endl;
      double a = numberStack.top() + 0.0;
      numberStack.pop();
      //cout << a << " is: " << typeid(a).name() << endl;

      // operation result
      double result = 0;

      // executing operation
      if (token == "+") result = a + b;           // addition
      else if (token == "-") result = a - b;      // subtraction
      else if (token == "*") result = a * b;      // multiplication
      else if (token == "/") result = a / b;      // division
      else if (token == "^") result = pow(a, b);  // exponentiation
      // checking for invalid operators
      else {
        cout << "INVALID INPUT - This operator doesn't exist\n";
        exit(1);
      }

      // pushing result to number stack
      numberStack.push(result);
    }
  }

  // cheking the size of the number stack
  int stackSize = numberStack.size();

  // If we got anything other than one, something went wrong.
  if (stackSize != 1) {
    cout << "SYNTAX ERROR - Not enough operators\n";
    exit(1);
  }

  // Pop result and print it
  double result = numberStack.top();
  numberStack.pop();

  return result;
}





// list of test expressions & expected output
vector< pair<string, double> > tests{
  /*1*/{"2 3 +", 5},
  /*2*/{"2 3 *", 6},
  /*3*/{"-123123 -123123 +", -246246},
  /*4*/{"2 2 + 3 - 123 - 24 *", -2928},
  /*5*/{"5 3 + 7 15 * 8 + -", -105},
  /*6*/{"2.00 3.99 +", 5.99},
  /*7*/{"4.00 2.00 /", 2.00},
  /*8*/{"2.0 5.0 ^", 32.0},
};





// run test expressions
bool runTests() {
  bool success = true;

  // looping through tests
  for (auto test : tests) {
    // solve expression
    double result = solve(test.first);

    // comparing solve() output with expected output
    if (result != test.second) {
      // current test failed
      cout << "FAILED TEST " << test.first << endl;
      cout << "EXPECTED " << test.second << " GOT " << result << endl;
      success = false;
    }
  }
  return success;
}






// main program function
int main() {

  // run test expressions
  bool success = runTests();
  if (success) cout << "SUCCESS!";


  string expression;

  // read RPN expression
  cout << "Enter RPN: ";
  getline(cin, expression);

  // evaluate expression
  double result = solve(expression);

  // print output
  cout << result << endl;
  

  return 0;
}

Finally, I’m going to try out some Javascript. This is the classic memoized Fibonacci sequence program in JS:

var fibList = [];

function fib(num) {
  if (num == 1 || num == 2) {
    return 1;
  } else {
    return fib(num-1) + fib(num-2);
  }
}

function main(num){
  console.log("The first " + num + "Fibonacci numbers are: ");
  for (var i = 1; i < num+1; i++) {
    fibNum = fib(i);
    console.log("Fib number " + i + ": " + fibNum);
  }
}

main(50);

Yep, Prism.js is definetely working as advertised. 😃

Expect to see it used quite regularly in my future posts.