830 lines
22 KiB
CoffeeScript
830 lines
22 KiB
CoffeeScript
# This scanner uses the recursive descent method.
|
|
#
|
|
# The char pointers token_str and scan_str are pointers to the input string as
|
|
# in the following example.
|
|
#
|
|
# | g | a | m | m | a | | a | l | p | h | a |
|
|
# ^ ^
|
|
# token_str scan_str
|
|
#
|
|
# The char pointer token_buf points to a malloc buffer.
|
|
#
|
|
# | g | a | m | m | a | \0 |
|
|
# ^
|
|
# token_buf
|
|
#
|
|
# In the sequence of method invocations for scanning,
|
|
# first we do the calls for scanning the operands
|
|
# of the operators of least precedence.
|
|
# So, since precedence in maths goes something like
|
|
# (form high to low) exponents, mult/div, plus/minus
|
|
# so we scan first for terms, then factors, then powers.
|
|
# That's the general idea, but of course we also have to deal
|
|
# with things like parens, non-commutative
|
|
# dot (or inner) product, assignments and tests,
|
|
# function calls etc.
|
|
# Note that a^1/2 is, correctly, a/2, not, incorrectly, sqrt(a),
|
|
# see comment in related test in power.coffee for more about this.
|
|
|
|
# Notes:
|
|
#
|
|
# Formerly add() and multiply() were used to construct expressions but
|
|
# this preevaluation caused problems.
|
|
#
|
|
# For example, suppose A has the floating point value inf.
|
|
#
|
|
# Before, the expression A/A resulted in 1 because the scanner would
|
|
# divide the symbols.
|
|
#
|
|
# After removing add() and multiply(), A/A results in nan which is the
|
|
# correct result.
|
|
#
|
|
# The functions negate() and inverse() are used but they do not cause
|
|
# problems with preevaluation of symbols.
|
|
|
|
|
|
T_INTEGER = 1001
|
|
T_DOUBLE = 1002
|
|
T_SYMBOL = 1003
|
|
T_FUNCTION = 1004
|
|
T_NEWLINE = 1006
|
|
T_STRING = 1007
|
|
T_GTEQ = 1008
|
|
T_LTEQ = 1009
|
|
T_EQ = 1010
|
|
T_NEQ = 1011
|
|
T_QUOTASSIGN = 1012
|
|
|
|
token = ""
|
|
newline_flag = 0
|
|
meta_mode = 0
|
|
|
|
input_str = 0
|
|
scan_str = 0
|
|
token_str = 0
|
|
token_buf = 0
|
|
|
|
lastFoundSymbol = null
|
|
symbolsRightOfAssignment = null
|
|
symbolsLeftOfAssignment = null
|
|
isSymbolLeftOfAssignment = null
|
|
scanningParameters = null
|
|
functionInvokationsScanningStack = null
|
|
skipRootVariableToBeSolved = false
|
|
assignmentFound = null
|
|
|
|
|
|
# Returns number of chars scanned and expr on stack.
|
|
|
|
# Returns zero when nothing left to scan.
|
|
|
|
# takes a string
|
|
|
|
scanned = ""
|
|
scan = (s) ->
|
|
if DEBUG then console.log "#### scanning " + s
|
|
#if s=="y=x"
|
|
# debugger
|
|
#if s=="y"
|
|
# debugger
|
|
#if s=="i=sqrt(-1)"
|
|
# debugger
|
|
|
|
lastFoundSymbol = null
|
|
symbolsRightOfAssignment = []
|
|
symbolsLeftOfAssignment = []
|
|
isSymbolLeftOfAssignment = true
|
|
scanningParameters = []
|
|
functionInvokationsScanningStack = [""]
|
|
assignmentFound = false
|
|
|
|
|
|
scanned = s
|
|
meta_mode = 0
|
|
expanding++
|
|
input_str = 0
|
|
scan_str = 0
|
|
get_next_token()
|
|
if (token == "")
|
|
push(symbol(NIL))
|
|
expanding--
|
|
return 0
|
|
scan_stmt()
|
|
expanding--
|
|
|
|
if !assignmentFound
|
|
symbolsInExpressionsWithoutAssignments = symbolsInExpressionsWithoutAssignments.concat symbolsLeftOfAssignment
|
|
|
|
return token_str - input_str
|
|
|
|
# takes a string
|
|
scan_meta = (s) ->
|
|
scanned = s
|
|
meta_mode = 1
|
|
expanding++
|
|
input_str = 0
|
|
scan_str = 0
|
|
get_next_token()
|
|
if (token == "")
|
|
push(symbol(NIL))
|
|
expanding--
|
|
return 0
|
|
scan_stmt()
|
|
expanding--
|
|
return token_str - input_str
|
|
|
|
scan_stmt = ->
|
|
scan_relation()
|
|
|
|
assignmentIsOfQuotedType = false
|
|
|
|
if token == T_QUOTASSIGN
|
|
assignmentIsOfQuotedType = true
|
|
|
|
if (token == T_QUOTASSIGN or token == '=')
|
|
symbolLeftOfAssignment = lastFoundSymbol
|
|
if DEBUG then console.log("assignment!")
|
|
assignmentFound = true
|
|
isSymbolLeftOfAssignment = false
|
|
|
|
get_next_token()
|
|
push_symbol(SETQ)
|
|
swap()
|
|
|
|
# if it's a := then add a quote
|
|
if (assignmentIsOfQuotedType)
|
|
push_symbol(QUOTE)
|
|
|
|
scan_relation()
|
|
|
|
# if it's a := then you have to list
|
|
# together the quote and its argument
|
|
if assignmentIsOfQuotedType
|
|
list(2)
|
|
|
|
list(3)
|
|
|
|
isSymbolLeftOfAssignment = true
|
|
|
|
if codeGen
|
|
|
|
# in case of re-assignment, the symbol on the
|
|
# left will also be in the set of the symbols
|
|
# on the right. In that case just remove it from
|
|
# the symbols on the right.
|
|
indexOfSymbolLeftOfAssignment = symbolsRightOfAssignment.indexOf(symbolLeftOfAssignment)
|
|
if indexOfSymbolLeftOfAssignment != -1
|
|
symbolsRightOfAssignment.splice(indexOfSymbolLeftOfAssignment, 1)
|
|
symbolsHavingReassignments.push symbolLeftOfAssignment
|
|
|
|
# print out the immediate dependencies
|
|
if DEBUG
|
|
console.log "locally, " + symbolLeftOfAssignment + " depends on: "
|
|
for i in symbolsRightOfAssignment
|
|
console.log " " + i
|
|
|
|
# ok add the local dependencies to the existing
|
|
# dependencies of this left-value symbol
|
|
|
|
# create the exiting dependencies list if it doesn't exist
|
|
symbolsDependencies[symbolLeftOfAssignment] ?= []
|
|
existingDependencies = symbolsDependencies[symbolLeftOfAssignment]
|
|
|
|
# copy over the new dependencies to the existing
|
|
# dependencies avoiding repetitions
|
|
for i in symbolsRightOfAssignment
|
|
if existingDependencies.indexOf(i) == -1
|
|
existingDependencies.push i
|
|
|
|
symbolsRightOfAssignment = []
|
|
|
|
scan_relation = ->
|
|
scan_expression()
|
|
switch (token)
|
|
when T_EQ
|
|
push_symbol(TESTEQ)
|
|
swap()
|
|
get_next_token()
|
|
scan_expression()
|
|
list(3)
|
|
when T_NEQ
|
|
push_symbol(NOT)
|
|
swap()
|
|
push_symbol(TESTEQ)
|
|
swap()
|
|
get_next_token()
|
|
scan_expression()
|
|
list(3)
|
|
list(2)
|
|
when T_LTEQ
|
|
push_symbol(TESTLE)
|
|
swap()
|
|
get_next_token()
|
|
scan_expression()
|
|
list(3)
|
|
when T_GTEQ
|
|
push_symbol(TESTGE)
|
|
swap()
|
|
get_next_token()
|
|
scan_expression()
|
|
list(3)
|
|
when '<'
|
|
push_symbol(TESTLT)
|
|
swap()
|
|
get_next_token()
|
|
scan_expression()
|
|
list(3)
|
|
when '>'
|
|
push_symbol(TESTGT)
|
|
swap()
|
|
get_next_token()
|
|
scan_expression()
|
|
list(3)
|
|
|
|
scan_expression = ->
|
|
h = tos
|
|
switch token
|
|
when '+'
|
|
get_next_token()
|
|
scan_term()
|
|
when '-'
|
|
get_next_token()
|
|
scan_term()
|
|
negate()
|
|
else
|
|
scan_term()
|
|
|
|
while (newline_flag == 0 && (token == '+' || token == '-'))
|
|
if (token == '+')
|
|
get_next_token()
|
|
scan_term()
|
|
else
|
|
get_next_token()
|
|
scan_term()
|
|
negate()
|
|
|
|
if (tos - h > 1)
|
|
list(tos - h)
|
|
push_symbol(ADD)
|
|
swap()
|
|
cons()
|
|
|
|
is_factor = ->
|
|
|
|
if token.charCodeAt?(0) == dotprod_unicode
|
|
return 1
|
|
|
|
switch (token)
|
|
when '*', '/'
|
|
return 1
|
|
when '(', T_SYMBOL, T_FUNCTION, T_INTEGER, T_DOUBLE, T_STRING
|
|
if (newline_flag) # implicit mul can't cross line
|
|
scan_str = token_str # better error display
|
|
return 0
|
|
else
|
|
return 1
|
|
return 0
|
|
|
|
|
|
simplify_1_in_products = (tos,h) ->
|
|
if (tos > h && isrational(stack[tos - 1]) && equaln(stack[tos - 1], 1))
|
|
pop()
|
|
|
|
# calculate away consecutive constants
|
|
multiply_consecutive_constants = (tos,h)->
|
|
if (tos > h + 1 && isNumericAtom(stack[tos - 2]) && isNumericAtom(stack[tos - 1]))
|
|
multiply()
|
|
|
|
|
|
scan_term = ->
|
|
h = tos
|
|
|
|
scan_factor()
|
|
|
|
if parse_time_simplifications
|
|
simplify_1_in_products(tos,h)
|
|
|
|
while (is_factor())
|
|
if (token == '*')
|
|
get_next_token()
|
|
scan_factor()
|
|
else if (token == '/')
|
|
# in case of 1/... then
|
|
# we scanned the 1, we get rid
|
|
# of it because otherwise it becomes
|
|
# an extra factor that wasn't there and
|
|
# things like
|
|
# 1/(2*a) become 1*(1/(2*a))
|
|
simplify_1_in_products(tos,h)
|
|
get_next_token()
|
|
scan_factor()
|
|
inverse()
|
|
else if (token.charCodeAt?(0) == dotprod_unicode)
|
|
get_next_token()
|
|
push_symbol(INNER)
|
|
swap()
|
|
scan_factor()
|
|
list(3)
|
|
|
|
else
|
|
scan_factor()
|
|
|
|
if parse_time_simplifications
|
|
multiply_consecutive_constants(tos,h)
|
|
simplify_1_in_products(tos,h)
|
|
|
|
if (h == tos)
|
|
push_integer(1)
|
|
else if (tos - h > 1)
|
|
list(tos - h)
|
|
push_symbol(MULTIPLY)
|
|
swap()
|
|
cons()
|
|
|
|
scan_power = ->
|
|
if (token == '^')
|
|
get_next_token()
|
|
push_symbol(POWER)
|
|
swap()
|
|
scan_factor()
|
|
list(3)
|
|
|
|
scan_index = (h) ->
|
|
#console.log "[ as index"
|
|
get_next_token()
|
|
push_symbol(INDEX)
|
|
swap()
|
|
scan_expression()
|
|
while (token == ',')
|
|
get_next_token()
|
|
scan_expression()
|
|
if (token != ']')
|
|
scan_error("] expected")
|
|
get_next_token()
|
|
list(tos - h)
|
|
|
|
|
|
scan_factor = ->
|
|
|
|
h = tos
|
|
|
|
#console.log "scan_factor token: " + token
|
|
|
|
firstFactorIsNumber = false
|
|
|
|
if (token == '(')
|
|
scan_subexpr()
|
|
else if (token == T_SYMBOL)
|
|
scan_symbol()
|
|
else if (token == T_FUNCTION)
|
|
scan_function_call_with_function_name()
|
|
else if token == '['
|
|
#console.log "[ as tensor"
|
|
#debugger
|
|
scan_tensor()
|
|
else if (token == T_INTEGER)
|
|
firstFactorIsNumber = true
|
|
bignum_scan_integer(token_buf)
|
|
get_next_token()
|
|
else if (token == T_DOUBLE)
|
|
firstFactorIsNumber = true
|
|
bignum_scan_float(token_buf)
|
|
get_next_token()
|
|
else if (token == T_STRING)
|
|
scan_string()
|
|
else
|
|
scan_error("syntax error")
|
|
|
|
|
|
# after the main initial part of the factor that
|
|
# we just scanned above,
|
|
# we can get an arbitrary about of appendages
|
|
# of the form ...[...](...)...
|
|
# If the main part is not a number, then these are all, respectively,
|
|
# - index references (as opposed to tensor definition) and
|
|
# - function calls without an explicit function name
|
|
# (instead of subexpressions or parameters of function
|
|
# definitions or function calls with an explicit function
|
|
# name), respectively
|
|
while token == '[' or token == '(' and newline_flag == 0 and !firstFactorIsNumber
|
|
if token == '['
|
|
scan_index(h)
|
|
else if token == '('
|
|
#console.log "( as function call without function name "
|
|
scan_function_call_without_function_name()
|
|
|
|
|
|
|
|
while (token == '!')
|
|
get_next_token()
|
|
push_symbol(FACTORIAL)
|
|
swap()
|
|
list(2)
|
|
|
|
# in theory we could already count the
|
|
# number of transposes and simplify them
|
|
# away, but it's not that clean to have
|
|
# multiple places where that happens, and
|
|
# the parser is not the place.
|
|
while (token.charCodeAt?(0) == transpose_unicode)
|
|
get_next_token()
|
|
push_symbol(TRANSPOSE)
|
|
swap()
|
|
list(2)
|
|
|
|
scan_power()
|
|
|
|
|
|
addSymbolRightOfAssignment = (theSymbol) ->
|
|
if predefinedSymbolsInGlobalScope_doNotTrackInDependencies.indexOf(theSymbol) == -1 and
|
|
symbolsRightOfAssignment.indexOf(theSymbol) == -1 and
|
|
symbolsRightOfAssignment.indexOf("'"+theSymbol) == -1 and
|
|
!skipRootVariableToBeSolved
|
|
if DEBUG then console.log("... adding symbol: " + theSymbol + " to the set of the symbols right of assignment")
|
|
prefixVar = ""
|
|
for i in [1...functionInvokationsScanningStack.length]
|
|
if functionInvokationsScanningStack[i] != ""
|
|
prefixVar += functionInvokationsScanningStack[i] + "_" + i + "_"
|
|
|
|
theSymbol = prefixVar + theSymbol
|
|
symbolsRightOfAssignment.push theSymbol
|
|
|
|
addSymbolLeftOfAssignment = (theSymbol) ->
|
|
if predefinedSymbolsInGlobalScope_doNotTrackInDependencies.indexOf(theSymbol) == -1 and
|
|
symbolsLeftOfAssignment.indexOf(theSymbol) == -1 and
|
|
symbolsLeftOfAssignment.indexOf("'"+theSymbol) == -1 and
|
|
!skipRootVariableToBeSolved
|
|
if DEBUG then console.log("... adding symbol: " + theSymbol + " to the set of the symbols left of assignment")
|
|
prefixVar = ""
|
|
for i in [1...functionInvokationsScanningStack.length]
|
|
if functionInvokationsScanningStack[i] != ""
|
|
prefixVar += functionInvokationsScanningStack[i] + "_" + i + "_"
|
|
|
|
theSymbol = prefixVar + theSymbol
|
|
symbolsLeftOfAssignment.push theSymbol
|
|
|
|
scan_symbol = ->
|
|
if (token != T_SYMBOL)
|
|
scan_error("symbol expected")
|
|
if (meta_mode && token_buf.length == 1)
|
|
switch (token_buf[0])
|
|
when 'a'
|
|
push(symbol(METAA))
|
|
when 'b'
|
|
push(symbol(METAB))
|
|
when 'x'
|
|
push(symbol(METAX))
|
|
else
|
|
push(usr_symbol(token_buf))
|
|
else
|
|
push(usr_symbol(token_buf))
|
|
#console.log "found symbol: " + token_buf
|
|
|
|
if scanningParameters.length == 0
|
|
if DEBUG then console.log "out of scanning parameters, processing " + token_buf
|
|
lastFoundSymbol = token_buf
|
|
if isSymbolLeftOfAssignment
|
|
addSymbolLeftOfAssignment token_buf
|
|
else
|
|
if DEBUG then console.log "still scanning parameters, skipping " + token_buf
|
|
if isSymbolLeftOfAssignment
|
|
addSymbolRightOfAssignment "'" + token_buf
|
|
|
|
if DEBUG then console.log("found symbol: " + token_buf + " left of assignment: " + isSymbolLeftOfAssignment)
|
|
|
|
# if we were looking at the right part of an assignment while we
|
|
# found the symbol, then add it to the "symbolsRightOfAssignment"
|
|
# set (we check for duplications)
|
|
if !isSymbolLeftOfAssignment
|
|
addSymbolRightOfAssignment token_buf
|
|
get_next_token()
|
|
|
|
scan_string = ->
|
|
push new_string(token_buf)
|
|
get_next_token()
|
|
|
|
scan_function_call_with_function_name = ->
|
|
if DEBUG then console.log "-- scan_function_call_with_function_name start"
|
|
n = 1 # the parameter number as we scan parameters
|
|
p = new U()
|
|
p = usr_symbol(token_buf)
|
|
|
|
push(p)
|
|
functionName = token_buf
|
|
if functionName == "roots" or functionName == "defint" or functionName == "sum" or functionName == "product" or functionName == "for"
|
|
functionInvokationsScanningStack.push token_buf
|
|
lastFoundSymbol = token_buf
|
|
if !isSymbolLeftOfAssignment
|
|
addSymbolRightOfAssignment token_buf
|
|
|
|
get_next_token() # open parens
|
|
get_next_token() # 1st parameter
|
|
scanningParameters.push true
|
|
if (token != ')')
|
|
scan_stmt()
|
|
n++
|
|
while (token == ',')
|
|
get_next_token()
|
|
# roots' disappearing variable, if there, is the second one
|
|
if n == 2 and functionInvokationsScanningStack[functionInvokationsScanningStack.length - 1].indexOf("roots") != -1
|
|
symbolsRightOfAssignment = symbolsRightOfAssignment.filter (x) -> !(new RegExp("roots_" + (functionInvokationsScanningStack.length - 1) + "_" + token_buf)).test(x)
|
|
skipRootVariableToBeSolved = true
|
|
# sums' disappearing variable, is alsways the second one
|
|
if n == 2 and functionInvokationsScanningStack[functionInvokationsScanningStack.length - 1].indexOf("sum") != -1
|
|
symbolsRightOfAssignment = symbolsRightOfAssignment.filter (x) -> !(new RegExp("sum_" + (functionInvokationsScanningStack.length - 1) + "_" + token_buf)).test(x)
|
|
skipRootVariableToBeSolved = true
|
|
# product's disappearing variable, is alsways the second one
|
|
if n == 2 and functionInvokationsScanningStack[functionInvokationsScanningStack.length - 1].indexOf("product") != -1
|
|
symbolsRightOfAssignment = symbolsRightOfAssignment.filter (x) -> !(new RegExp("product_" + (functionInvokationsScanningStack.length - 1) + "_" + token_buf)).test(x)
|
|
skipRootVariableToBeSolved = true
|
|
# for's disappearing variable, is alsways the second one
|
|
if n == 2 and functionInvokationsScanningStack[functionInvokationsScanningStack.length - 1].indexOf("for") != -1
|
|
symbolsRightOfAssignment = symbolsRightOfAssignment.filter (x) -> !(new RegExp("for_" + (functionInvokationsScanningStack.length - 1) + "_" + token_buf)).test(x)
|
|
skipRootVariableToBeSolved = true
|
|
# defint's disappearing variables can be in positions 2,5,8...
|
|
if functionInvokationsScanningStack[functionInvokationsScanningStack.length - 1].indexOf("defint") != -1 and
|
|
(n == 2 or (n>2 and ((n-2) % 3 == 0)))
|
|
symbolsRightOfAssignment = symbolsRightOfAssignment.filter (x) -> !(new RegExp("defint_" + (functionInvokationsScanningStack.length - 1) + "_" + token_buf)).test(x)
|
|
skipRootVariableToBeSolved = true
|
|
|
|
scan_stmt()
|
|
skipRootVariableToBeSolved = false
|
|
n++
|
|
|
|
# todo refactor this, there are two copies
|
|
# this catches the case where the "roots" variable is not specified
|
|
if n == 2 and functionInvokationsScanningStack[functionInvokationsScanningStack.length - 1].indexOf("roots") != -1
|
|
symbolsRightOfAssignment = symbolsRightOfAssignment.filter (x) -> !(new RegExp("roots_" + (functionInvokationsScanningStack.length - 1) + "_" + "x")).test(x)
|
|
|
|
scanningParameters.pop()
|
|
|
|
for i in [0..symbolsRightOfAssignment.length]
|
|
if symbolsRightOfAssignment[i]?
|
|
if functionName == "roots"
|
|
symbolsRightOfAssignment[i] = symbolsRightOfAssignment[i].replace(new RegExp("roots_" + (functionInvokationsScanningStack.length - 1) + "_"),"")
|
|
if functionName == "defint"
|
|
symbolsRightOfAssignment[i] = symbolsRightOfAssignment[i].replace(new RegExp("defint_" + (functionInvokationsScanningStack.length - 1) + "_"),"")
|
|
if functionName == "sum"
|
|
symbolsRightOfAssignment[i] = symbolsRightOfAssignment[i].replace(new RegExp("sum_" + (functionInvokationsScanningStack.length - 1) + "_"),"")
|
|
if functionName == "product"
|
|
symbolsRightOfAssignment[i] = symbolsRightOfAssignment[i].replace(new RegExp("product_" + (functionInvokationsScanningStack.length - 1) + "_"),"")
|
|
if functionName == "for"
|
|
symbolsRightOfAssignment[i] = symbolsRightOfAssignment[i].replace(new RegExp("for_" + (functionInvokationsScanningStack.length - 1) + "_"),"")
|
|
|
|
if (token != ')')
|
|
scan_error(") expected")
|
|
|
|
get_next_token()
|
|
list(n)
|
|
if functionName == "roots" or functionName == "defint" or functionName == "sum" or functionName == "product" or functionName == "for"
|
|
functionInvokationsScanningStack.pop()
|
|
if functionName == symbol(PATTERN).printname
|
|
patternHasBeenFound = true
|
|
|
|
if DEBUG then console.log "-- scan_function_call_with_function_name end"
|
|
|
|
scan_function_call_without_function_name = ->
|
|
if DEBUG then console.log "-- scan_function_call_without_function_name start"
|
|
|
|
# the function will have to be looked up
|
|
# at runtime (i.e. we need to evaulate something to find it
|
|
# e.g. it might be inside a tensor, so we'd need to evaluate
|
|
# a tensor element access in that case)
|
|
push_symbol(EVAL)
|
|
swap()
|
|
list(2)
|
|
|
|
n = 1 # the parameter number as we scan parameters
|
|
get_next_token() # left paren
|
|
scanningParameters.push true
|
|
if (token != ')')
|
|
scan_stmt()
|
|
n++
|
|
while (token == ',')
|
|
get_next_token()
|
|
scan_stmt()
|
|
n++
|
|
|
|
scanningParameters.pop()
|
|
|
|
|
|
if (token != ')')
|
|
scan_error(") expected")
|
|
|
|
get_next_token()
|
|
list(n)
|
|
|
|
if DEBUG then console.log "-- scan_function_call_without_function_name end: " + stack[tos-1]
|
|
|
|
# scan subexpression
|
|
|
|
scan_subexpr = ->
|
|
n = 0
|
|
if (token != '(')
|
|
scan_error("( expected")
|
|
get_next_token()
|
|
scan_stmt()
|
|
if (token != ')')
|
|
scan_error(") expected")
|
|
get_next_token()
|
|
|
|
scan_tensor = ->
|
|
n = 0
|
|
if (token != '[')
|
|
scan_error("[ expected")
|
|
|
|
get_next_token()
|
|
|
|
#console.log "scanning the next statement"
|
|
scan_stmt()
|
|
|
|
n = 1
|
|
while (token == ',')
|
|
get_next_token()
|
|
scan_stmt()
|
|
n++
|
|
|
|
#console.log "building tensor with elements number: " + n
|
|
build_tensor(n)
|
|
|
|
if (token != ']')
|
|
scan_error("] expected")
|
|
get_next_token()
|
|
|
|
scan_error = (errmsg) ->
|
|
errorMessage = ""
|
|
|
|
# try not to put question mark on orphan line
|
|
|
|
while (input_str != scan_str)
|
|
if ((scanned[input_str] == '\n' || scanned[input_str] == '\r') && input_str + 1 == scan_str)
|
|
break
|
|
errorMessage += scanned[input_str++]
|
|
|
|
errorMessage += " ? "
|
|
|
|
while (scanned[input_str] && (scanned[input_str] != '\n' && scanned[input_str] != '\r'))
|
|
errorMessage += scanned[input_str++]
|
|
|
|
errorMessage += '\n'
|
|
|
|
stop(errmsg)
|
|
|
|
# There are n expressions on the stack, possibly tensors.
|
|
#
|
|
# This function assembles the stack expressions into a single tensor.
|
|
#
|
|
# For example, at the top level of the expression ((a,b),(c,d)), the vectors
|
|
# (a,b) and (c,d) would be on the stack.
|
|
|
|
# takes an integer
|
|
build_tensor = (n) ->
|
|
# int i, j, k, ndim, nelem
|
|
|
|
i = 0
|
|
|
|
save()
|
|
|
|
p2 = alloc_tensor(n)
|
|
p2.tensor.ndim = 1
|
|
p2.tensor.dim[0] = n
|
|
for i in [0...n]
|
|
p2.tensor.elem[i] = stack[tos-n+i]
|
|
|
|
check_tensor_dimensions p2
|
|
|
|
moveTos tos - n
|
|
|
|
push(p2)
|
|
|
|
restore()
|
|
|
|
get_next_token = ->
|
|
newline_flag = 0
|
|
while (1)
|
|
get_token()
|
|
if (token != T_NEWLINE)
|
|
break
|
|
newline_flag = 1
|
|
if DEBUG then console.log "get_next_token token: " + token
|
|
#if token == ')'
|
|
# debugger
|
|
|
|
get_token = ->
|
|
# skip spaces
|
|
while (isspace(scanned[scan_str]))
|
|
if (scanned[scan_str] == '\n' || scanned[scan_str] == '\r')
|
|
token = T_NEWLINE
|
|
scan_str++
|
|
return
|
|
scan_str++
|
|
|
|
token_str = scan_str
|
|
|
|
# end of string?
|
|
|
|
if (scan_str == scanned.length)
|
|
token = ""
|
|
return
|
|
|
|
# number?
|
|
|
|
if (isdigit(scanned[scan_str]) || scanned[scan_str] == '.')
|
|
while (isdigit(scanned[scan_str]))
|
|
scan_str++
|
|
if (scanned[scan_str] == '.')
|
|
scan_str++
|
|
while (isdigit(scanned[scan_str]))
|
|
scan_str++
|
|
if (scanned[scan_str] == 'e' && (scanned[scan_str+1] == '+' || scanned[scan_str+1] == '-' || isdigit(scanned[scan_str+1])))
|
|
scan_str += 2
|
|
while (isdigit(scanned[scan_str]))
|
|
scan_str++
|
|
token = T_DOUBLE
|
|
else
|
|
token = T_INTEGER
|
|
update_token_buf(token_str, scan_str)
|
|
return
|
|
|
|
# symbol?
|
|
|
|
if (isalpha(scanned[scan_str]))
|
|
while (isalnumorunderscore(scanned[scan_str]))
|
|
scan_str++
|
|
if (scanned[scan_str] == '(')
|
|
token = T_FUNCTION
|
|
else
|
|
token = T_SYMBOL
|
|
update_token_buf(token_str, scan_str)
|
|
return
|
|
|
|
# string ?
|
|
|
|
if (scanned[scan_str] == '"')
|
|
scan_str++
|
|
while (scanned[scan_str] != '"')
|
|
#if (scan_str == scanned.length || scanned[scan_str] == '\n' || scanned[scan_str] == '\r')
|
|
if (scan_str == scanned.length - 1)
|
|
scan_str++
|
|
scan_error("runaway string")
|
|
scan_str--
|
|
scan_str++
|
|
scan_str++
|
|
token = T_STRING
|
|
update_token_buf(token_str + 1, scan_str - 1)
|
|
return
|
|
|
|
# comment?
|
|
|
|
if (scanned[scan_str] == '#' || scanned[scan_str] == '-' && scanned[scan_str+1] == '-')
|
|
while (scanned[scan_str] && scanned[scan_str] != '\n' && scanned[scan_str] != '\r')
|
|
scan_str++
|
|
if (scanned[scan_str])
|
|
scan_str++
|
|
token = T_NEWLINE
|
|
return
|
|
|
|
# quote-assignment
|
|
if (scanned[scan_str] == ':' && scanned[scan_str+1] == '=')
|
|
scan_str += 2
|
|
token = T_QUOTASSIGN
|
|
return
|
|
|
|
# relational operator?
|
|
if (scanned[scan_str] == '=' && scanned[scan_str+1] == '=')
|
|
scan_str += 2
|
|
token = T_EQ
|
|
return
|
|
|
|
# != operator. It's a little odd because
|
|
# "!" is not a "not", which would make things consistent.
|
|
# (it's used for factorial).
|
|
# An alternative would be to use "<>" but it's not used
|
|
# a lot in other languages...
|
|
if (scanned[scan_str] == '!' && scanned[scan_str+1] == '=')
|
|
scan_str += 2
|
|
token = T_NEQ
|
|
return
|
|
|
|
if (scanned[scan_str] == '<' && scanned[scan_str+1] == '=')
|
|
scan_str += 2
|
|
token = T_LTEQ
|
|
return
|
|
|
|
if (scanned[scan_str] == '>' && scanned[scan_str+1] == '=')
|
|
scan_str += 2
|
|
token = T_GTEQ
|
|
return
|
|
|
|
# single char token
|
|
|
|
token = scanned[scan_str++]
|
|
|
|
# both strings
|
|
update_token_buf = (a,b) ->
|
|
|
|
token_buf = scanned.substring(a,b)
|
|
|
|
|
|
$.scan = scan
|