Add first stage of a new single-file parser

This first stage consists of the general framework for the parser as
a whole, a lexical analyzers to converts text into commands, and the
semantic anlyzer to convert commands into a complete data structure
representing the cumulative effect of running the commands.
This commit is contained in:
Andrew Miner
2016-04-10 13:37:17 -07:00
parent 29315f03f7
commit 14278c81df
17 changed files with 1203 additions and 0 deletions
@@ -0,0 +1,105 @@
#
# Crafting Guide - parser_extension.coffee
#
# Copyright © 2014-2016 by Redwood Labs
# All rights reserved.
#
########################################################################################################################
module.exports = class ParserExtension
constructor: (state)->
@state = state
# Public Methods ###############################################################################
accepts: (command)->
return @_findCommandMethod(command)?
execute: w.lift (command)->
if not @_state? then throw new Error '@state must be defined before executing commands'
method = @_findCommandMethod command
method.call this, command
# Error Checking Helpers #######################################################################
alreadyExists: (command, type, id)->
obj = @state.get(type, id)
if obj?
@state.addError command, "a #{type} called #{id} has already been declared"
return true
return false
duplicateField: (command, obj, field)->
if obj[field]?
@state.addError command, "duplicate #{field}"
return true
return false
missingCurrent: (command, type)->
obj = @state.getCurrent type
if not obj?
@state.addError command, "you must declare a #{type} before using #{command.name}"
return true
return false
missingArgs: (command)->
if not command.args or command.args.length is 0
@state.addError command, "#{command.name} requires at least one item"
return true
return false
missingArgText: (command)->
if command.argText.length is 0
@state.addError command, "#{command.name} cannot be empty"
return true
return false
# Parsing Helpers ##############################################################################
parseBoolean: (command, text)->
return null unless text?
switch text.toLowerCase()
when "yes" then return true
when "no" then return false
@state.addError command, "#{command.name} requires \"yes\" or \"no\""
return null
parseInt: (command, text)->
return null unless text?
value = Number.parseInt text
if isNaN value
@addError command, "#{command.name} requires a number"
return null
return value
# Property Methods #############################################################################
getState: ->
return @_state
setState: (state)->
if not state? then throw new Error 'state cannot be undefined'
@_state = state
Object.defineProperties @prototype,
state: { get:@::getState, set:@::setState }
# Private Methods ##############################################################################
_findCommandMethod: (command)->
methodName = "_command_#{command.name}"
method = this[methodName]
return null unless _.isFunction(method)
return method