From e6b4a735593cdc82219716bb87fa4b5a3d48fdbc Mon Sep 17 00:00:00 2001 From: "Elf M. Sternberg" Date: Thu, 23 Jul 2015 16:17:20 -0700 Subject: [PATCH] Fail. --- chapter1/reader.coffee | 84 +++++++++++++++++++++++++++--------------- 1 file changed, 54 insertions(+), 30 deletions(-) diff --git a/chapter1/reader.coffee b/chapter1/reader.coffee index b4be16e..db5694d 100644 --- a/chapter1/reader.coffee +++ b/chapter1/reader.coffee @@ -1,13 +1,19 @@ {car, cdr, cons, nil, nilp, pairp, vectorToList, list} = require 'cons-lists/lists' +{length} = require 'cons-lists/reduce' {inspect} = require "util" - NEWLINES = ["\n", "\r", "\x0B", "\x0C"] WHITESPACE = [" ", "\t"].concat(NEWLINES) EOF = new (class Eof)() EOO = new (class Eoo)() +stringp = (o) -> o?.__type == 'string' + +errorp = (o) -> (pairp o) and (car o) == 'error' + +streq = (s, t) -> String(s) == String(t) + class Source constructor: (@inStream) -> @index = 0 @@ -32,13 +38,24 @@ class Source skipWS = (inStream) -> while inStream.peek() in WHITESPACE then inStream.next() +invisibleProperty = (obj, name, value) -> + Object.defineProperty obj, name, + value: value + configurable: false + enumerable: false + writable: false + obj + +lineInfo = (obj, line, column) -> + invisibleProperty obj, '__position', {line: line, column: column} + # (type, value, line, column) -> (node {type, value, line, column)} -makeObj = (type, value, line, column) -> - list(type, value, line, column) +makeObj = (value, type, line, column) -> + lineInfo (invisibleProperty value, '__type', type), line, column # msg -> (IO -> Node => Error) handleError = (message) -> - (line, column) -> makeObj('error', message, line, column) + (line, column) -> lineInfo (cons "error", message), line, column # IO -> Node => Comment readComment = (inStream) -> @@ -47,26 +64,26 @@ readComment = (inStream) -> inStream.next()).join("") if not inStream.done() inStream.next() - makeObj 'comment', r, line, column + makeObj (new String r), 'comment', line, column # IO -> (Node => Literal => String) | Error readString = (inStream) -> [line, column] = inStream.position() inStream.next() - string = until inStream.peek() == '"' or inStream.done() - if inStream.peek() == '\\' + string = until streq(inStream.peek(), '"') or inStream.done() + if (streq inStream.peek(), '\\') inStream.next() inStream.next() if inStream.done() return handleError("end of file seen before end of string.")(line, column) inStream.next() - makeObj 'string', (string.join ''), line, column + makeObj (new String (string.join '')), 'string', line, column # (String) -> (Node => Literal => Number) | Nothing readMaybeNumber = (symbol) -> - if symbol[0] == '+' + if streq(symbol[0], '+') return readMaybeNumber symbol.substr(1) - if symbol[0] == '-' + if streq(symbol[0], '-') ret = readMaybeNumber symbol.substr(1) return if ret? then -1 * ret else undefined if symbol.search(/^0x[0-9a-fA-F]+$/) > -1 @@ -85,43 +102,48 @@ readSymbol = (inStream, tableKeys) -> symbol = (until (inStream.done() or inStream.peek() in tableKeys or inStream.peek() in WHITESPACE) inStream.next()).join '' number = readMaybeNumber symbol + if nilp number + return nil if number? - return makeObj 'number', number, line, column - makeObj 'symbol', symbol, line, column + return makeObj (new Number number), 'number', line, column + makeObj (new String symbol), 'symbol', line, column # (Delim, TypeName) -> IO -> (IO, node) | Error -makeReadPair = (delim, type) -> +makeReadContainer = (delim, constructor) -> # IO -> (IO, Node) | Error (inStream) -> inStream.next() skipWS inStream [line, column] = inStream.position() - if inStream.peek() == delim + if streq(inStream.peek(), delim) inStream.next() - return makeObj(type, nil, line, column) + return constructor(nil, line, column) # IO -> (IO, Node) | Error dotted = false - readEachPair = (inStream) -> + readInContainer = (inStream) -> [line, column] = inStream.position() obj = read inStream, true, null, true - if inStream.peek() == delim + if streq(inStream.peek(), delim) if dotted then return obj - return cons obj, nil + return lineInfo (cons obj, nil), line, column if inStream.done() then return handleError("Unexpected end of input")(line, column) if dotted then return handleError("More than one symbol after dot") - return obj if (car obj) == 'error' - if (car obj) == 'symbol' and (car cdr obj) == '.' + return obj if (errorp obj) + if streq(obj, ".") dotted = true - return readEachPair inStream - cons obj, readEachPair inStream + return readInContainer inStream + cons obj, readInContainer inStream - ret = makeObj type, readEachPair(inStream), line, column + ret = readInContainer(inStream) inStream.next() ret # Type -> (IO -> (IO, Node)) +# +# Handles the quoted symbol things. +# prefixReader = (type) -> # IO -> (IO, Node) (inStream) -> @@ -130,7 +152,7 @@ prefixReader = (type) -> [line1, column1] = inStream.position() obj = read inStream, true, null, true return obj if (car obj) == 'error' - makeObj "list", cons((makeObj("symbol", type, line1, column1)), cons(obj)), line, column + cons (makeObj (new String type), 'symbol', line1, column1), obj # I really wanted to make anything more complex than a list (like an # object or a vector) something handled by a read macro. Maybe in a @@ -138,12 +160,14 @@ prefixReader = (type) -> readMacros = '"': readString - '(': makeReadPair ')', 'list' + '(': makeReadContainer ')', (o, l, c) -> lineInfo o, l, c ')': handleError "Closing paren encountered" - '[': makeReadPair ']', 'vector' + '[': makeReadContainer ']', (o, l, c) -> cons("vector", lineinfo o, l, c) ']': handleError "Closing bracket encountered" - '{': makeReadPair('}', 'record', (res) -> - res.length % 2 == 0 and true or mkerr "record key without value") + '{': makeReadContainer '}', (o, l, c) -> + if length(o) % 2 != 0 + return handleError "Records require an even number of items." + cons('record', lineinfo o, l, c) '}': handleError "Closing curly without corresponding opening." "`": prefixReader 'back-quote' "'": prefixReader 'quote' @@ -153,9 +177,9 @@ readMacros = # Given a stream, reads from the stream until a single complete lisp # object has been found and returns the object - # IO -> Form -read = (inStream, eofErrorP = false, eofError = EOF, recursiveP = false, inReadMacros = null, keepComments = false) -> +read = (inStream, eofErrorP = false, eofError = EOF, + recursiveP = false, inReadMacros = null, keepComments = false) -> inStream = if inStream instanceof Source then inStream else new Source inStream inReadMacros = if InReadMacros? then inReadMacros else readMacros inReadMacroKeys = (i for i of inReadMacros)