Demo entry 6755002

tcl in js - MIT

   

Submitted by anonymous on Jul 23, 2018 at 15:40
Language: JavaScript. Code size: 4.4 kB.

/// tcl.js

(function(){
 function Token(type, value){
  this.type = type
  this.value = value
  this.toString = this.toJSON = function() {
   return this.type + ' (\x60' + this.value + '\x60)'
  }
  return this
 }
 function d(o) {
  console.log(JSON.stringify(o))
 }
 function Lexer(text){
  this.chars = text.split('')
  this.pos = 0
  this.c = this.chars[this.pos]
  d('start')
  d(this.chars)
  return this
 }
 Lexer.prototype = {
  constructor: Lexer,
  advance: function(){
   if(this.chars.length <= this.pos) throw 'Out of bound index'
   d('at '+ (this.pos+1))
   return (this.c = this.chars[++this.pos])
  },
  error: function(o){
   throw o + ' at ' + this.pos
  },
  skip: function(){
   d('skip at '+ this.pos)
   if(this.c == '#')
    while(this.c != "\n")
     this.advance()
   while(/\s/.test(this.c) && this.c != "\n")
    this.advance()
   if(this.c == "\n")
    return this.advance(), new Token('NEWLINE', "\\n")
  },
  peek: function(n) {
   n = n || 1
   return this.chars[n + this.pos]
  },
  number: function(){
   for(var r = ''; /\d|\./.test(this.c); this.advance()) r += this.c
   return new Token('NUMBER', Number(r))
  },
  escape: function() {
   d('escape at '+ this.pos)
   if (this.c != '\\') return
   var c = this.advance(), r
   switch (c) {
   case 'a': r = 0x7 ;break;
   case 'b': r = 0x8 ;break;
   case 'f': r = 0xc ;break;
   case 'n': r = 0xa ;break;
   case 'r': r = 0xd ;break;
   case 't': r = 0x9 ;break;
   case 'v': r = 0xb ;break;
   case'\\':r = 0x5c ;break;
   }
   if (r) {
    // this.advance()
    return String.fromCharCode(r)
   }
   if (c == 'x') {
    r = this.advance()
    r += this.advance()
    // this.advance()
    try {
     return String.fromCharCode(parseInt(r, 16))
    } catch (e) {
     this.error('Invalid escape sequence \\x'+r)
    }
   }
   if (c == 'u') {
    r = this.advance()
    r += this.advance()
    r += this.advance()
    r += this.advance()
    // this.advance()
    try {
     return String.fromCharCode(parseInt(r, 16))
    } catch (e) {
     this.error('Invalid escape sequence \\u'+r)
    } 
   }
   var oc = /[0-7]/
   if (oc.test(c)) {
    r = c
    if (oc.test(this.peek())) {
     this.advance()
     r += this.c
     if (oc.test(this.peek())) {
      this.advance()
      r += this.c
      // this.advance()
     }
    }
    try {
     return String.fromCharCode(parseInt(r, 8))
    } catch (e) {
     this.error('Invalid octal escape sequence \\'+r)
    }
   }
   if (c == "\n") {
    this.advance()
    for(;this.c != "\n"; this.advance()) {
     if(this.c == '\\') this.escape()
    }
   }
   // this.advance()
   return c
  },
  quote: function() {
   d('quote at '+ this.pos)
   if(this.c == '"') {
    var delim = this.c
    this.advance()
    for(var str = ""; this.c != delim; this.advance()) {
     if (this.c == '\\') {
      str += this.escape()
     }
     else
      str += this.c
    }
    this.advance()
    return new Token('QUOTE', str)
   }
   if(this.c == '{') {
    var delim = '}'
    this.advance()
    for(var str = '', bc = 0; this.c != delim || bc; this.advance()) {
     if (this.c == '{') bc++
     if (this.c == '}') bc--
     if (this.c == '\\') {
      str += this.escape()
      continue
     }
     str += this.c
    }
    this.advance()
    return new Token('QUOTE', str)
   }
   if (this.c == '[') {
    this.advance()
    for (var com = '', bc = 0; this.c != ']' || bc ; this.advance()) {
     if (this.c == '[') bc++
     if (this.c == ']') bc--
     if (this.c == '\\') {
      com += this.escape()
      continue
     }
     com += this.c
    }
    this.advance()
    return new Token('COMMAND', com)
   }
  },
  word: function(){
   d('word at '+ this.pos)
   for(var w=''; /\S/.test(this.c); this.advance()) {
    if (this.c == '\\') {
     w += this.escape()
     continue
    }
    w += this.c
   }
   return new Token('WORD', w)
  },
  next: function() {
   d('next at '+ this.pos)
   if(this.chars.length <= this.pos) return
   var r = this.skip()
   if(r) return r
   if(/\d|\./.test(this.c))
    return this.number()
   else if(this.c == '"' || this.c == '{' || this.c == '[')
    return this.quote()
   else if(/\S/.test(this.c))
    return this.word()
  },
  all: function() {
   for(var a = [], c;c = this.next(); a.push(c)) {d('all at '+ this.pos),d(c)}
   return a
  }
 }
 lexer = function(txt) {
  return (new Lexer(txt)).all()
 }
})()

This snippet took 0.02 seconds to highlight.

Back to the Entry List or Home.

Delete this entry (admin only).