algebrite
Version:
Computer Algebra System in Coffeescript
915 lines (733 loc) • 32 kB
text/coffeescript
#jmp_buf stop_return, draw_stop_return
# s is a string here
stop = (s) ->
#if (draw_flag == 2)
# longjmp(draw_stop_return, 1)
#else
errorMessage += "Stop: "
errorMessage += s
#debugger
message = errorMessage
errorMessage = ''
moveTos 0
throw new Error(message)
#longjmp(stop_return, 1)
# Figuring out dependencies is key to automatically
# generating a method signature when generating JS code
# from algebrite scripts.
# This is important because the user can keep using normal Algebrite
# scripting without special notations.
# Basically the process consists of figuring out
# the "ground variables" that are needed to compute each variable.
# Now there are two ways of doing this:
# * at parse time
# * after running the scripts
# Doing it at parse time means that we can't track simplifications
# canceling-out some variables for example. But on the other side
# it's very quick and the user can somehow see what the signature is
# going to look like (assuming tha code is rather simple), or anyways
# is going to easily make sense of the generated signature.
# Doing it after execution on the other hand would allow us to see
# if some variable cancel-out. But if variables cancel out then
# they might do so according to some run-time behaviour that the user
# might struggle to keep track of.
# So the effort for the user to make sense of the signature in the first case
# is similar to the effort of keeping tab of types in a typed language.
# While in the second case the effort is similar to running the
# code and simplifications in her head.
#
# If we just want to compute the dependencies, we don't need to do
# anything costly, we don't "run" the code and we don't simplify
# the code. Just finding the plain dependencies
# TODO change the name of this function, as it doesn't just find the
# dependencies. It also runs it and generates the JS code.
findDependenciesInScript = (stringToBeParsed, dontGenerateCode) ->
if DEBUG then console.log "stringToBeParsed: " + stringToBeParsed
timeStartFromAlgebra = new Date().getTime()
if CACHE_DEBUGS or CACHE_HITSMISS_DEBUGS or TIMING_DEBUGS
console.log " --------- findDependenciesInScript input: " + stringToBeParsed + " at: " + (new Date())
currentStateHash = getStateHash()
console.log "state hash: " + currentStateHash
inited = true
codeGen = true
symbolsDependencies = {}
symbolsHavingReassignments = []
symbolsInExpressionsWithoutAssignments = []
patternHasBeenFound = false
indexOfPartRemainingToBeParsed = 0
allReturnedPlainStrings = ""
allReturnedLatexStrings = ""
n = 0
# we are going to store the dependencies _of the block as a whole_
# so all affected variables in the whole block are lumped
# together, and same for the variable that affect those, we
# lump them all together.
dependencyInfo =
affectsVariables: []
affectedBy: []
stringToBeRun = stringToBeParsed
if ENABLE_CACHING and stringToBeRun != "clearall"
currentStateHash = getStateHash()
cacheKey = currentStateHash + " stringToBeRun: " + stringToBeRun + " - " + dontGenerateCode
if CACHE_DEBUGS then console.log "cached_findDependenciesInScript key: " + cacheKey
possiblyCached = cached_findDependenciesInScript.get(cacheKey)
if possiblyCached?
if CACHE_HITSMISS_DEBUGS then console.log "cached_findDependenciesInScript hit on " + stringToBeRun
unfreeze(possiblyCached)
# return the output string
if TIMING_DEBUGS
totalTime = new Date().getTime() - timeStartFromAlgebra
console.log "findDependenciesInScript input: " + stringToBeRun + " time: " + totalTime + "ms, saved " + (possiblyCached[possiblyCached.length-5] - totalTime) + "ms due to cache hit"
return [
possiblyCached[possiblyCached.length - 7],
possiblyCached[possiblyCached.length - 6],
possiblyCached[possiblyCached.length - 5],
possiblyCached[possiblyCached.length - 4],
possiblyCached[possiblyCached.length - 3],
possiblyCached[possiblyCached.length - 2],
possiblyCached[possiblyCached.length - 1]
]
else
if CACHE_HITSMISS_DEBUGS then console.log "cached_findDependenciesInScript miss on: " + stringToBeRun
if TIMING_DEBUGS
cacheMissPenalty = (new Date().getTime() - timeStartFromAlgebra)
# parse the input. This collects the
# dependency information
while (1)
try
errorMessage = ""
check_stack()
if DEBUG then console.log "findDependenciesInScript: scanning"
n = scan(stringToBeParsed.substring(indexOfPartRemainingToBeParsed))
if DEBUG then console.log "scanned"
pop()
check_stack()
catch error
if PRINTOUTRESULT then console.log error
errorMessage = error + ""
#debugger
reset_after_error()
break
if (n == 0)
break
indexOfPartRemainingToBeParsed += n
testableString = ""
# print out all local dependencies as collected by this
# parsing pass
if DEBUG then console.log "all local dependencies ----------------"
testableString += "All local dependencies: "
for key, value of symbolsDependencies
if DEBUG then console.log "variable " + key + " depends on: "
dependencyInfo.affectsVariables.push key
testableString += " variable " + key + " depends on: "
for i in value
if DEBUG then console.log " " + i
if i[0] != "'"
dependencyInfo.affectedBy.push i
testableString += i + ", "
testableString += "; "
testableString += ". "
# print out the symbols with re-assignments:
if DEBUG then console.log "Symbols with reassignments ----------------"
testableString += "Symbols with reassignments: "
for key in symbolsHavingReassignments
if dependencyInfo.affectedBy.indexOf(key) == -1
dependencyInfo.affectedBy.push key
testableString += key + ", "
testableString += ". "
# print out the symbols that appear in expressions without assignments
if DEBUG then console.log "Symbols in expressions without assignments ----------------"
testableString += "Symbols in expressions without assignments: "
for key in symbolsInExpressionsWithoutAssignments
if dependencyInfo.affectedBy.indexOf(key) == -1
dependencyInfo.affectedBy.push key
testableString += key + ", "
testableString += ". "
# ALL Algebrite code is affected by any pattern changing
dependencyInfo.affectedBy.push "PATTERN_DEPENDENCY"
if patternHasBeenFound
dependencyInfo.affectsVariables.push "PATTERN_DEPENDENCY"
testableString += " - PATTERN_DEPENDENCY inserted - "
# print out all global dependencies as collected by this
# parsing pass
if DEBUG then console.log "All dependencies recursively ----------------"
testableString += "All dependencies recursively: "
scriptEvaluation = ["",""]
generatedCode = ""
readableSummaryOfGeneratedCode = ""
if errorMessage == "" and !dontGenerateCode
try
allReturnedPlainStrings = ""
allReturnedLatexStrings = ""
scriptEvaluation = run(stringToBeParsed, true)
allReturnedPlainStrings = ""
allReturnedLatexStrings = ""
catch error
if PRINTOUTRESULT then console.log error
errorMessage = error + ""
#debugger
init()
if errorMessage == ""
for key of symbolsDependencies
codeGen = true
if DEBUG then console.log " variable " + key + " is: " + get_binding(usr_symbol(key)).toString()
codeGen = false
if DEBUG then console.log " variable " + key + " depends on: "
testableString += " variable " + key + " depends on: "
recursedDependencies = []
variablesWithCycles = []
cyclesDescriptions = []
recursiveDependencies key, recursedDependencies, [], variablesWithCycles, [], cyclesDescriptions
for i in variablesWithCycles
if DEBUG then console.log " --> cycle through " + i
for i in recursedDependencies
if DEBUG then console.log " " + i
testableString += i + ", "
testableString += "; "
for i in cyclesDescriptions
testableString += " " + i + ", "
if DEBUG then console.log " code generation:" + key + " is: " + get_binding(usr_symbol(key)).toString()
# we really want to make an extra effort
# to generate simplified code, so
# run a "simplify" on the content of each
# variable that we are generating code for.
# Note that the variable
# will still point to un-simplified structures,
# we only simplify the generated code.
push get_binding(usr_symbol(key))
# Since we go and simplify all variables we meet,
# we have to replace each variable passed as a parameter
# with something entirely new, so that there is no chance
# that it might evoke previous values in the external scope
# as in this case:
# a = 2
# f(a) = a+1+b
# we don't want 'a' in the body of f to be simplified to 2
# There are two cases: 1) the variable actually was already in
# the symbol table, in which case there is going to be this new
# one prepended with AVOID_BINDING_TO_EXTERNAL_SCOPE_VALUE, and
# we'll have to remove up this variable later.
# OR 2) the variable wasn't already in the symbol table, in which
# case we directly create this one, which means that we'll have
# to rename it later to the correct name without the prepended
# part.
replacementsFrom = []
replacementsTo = []
for eachDependency in recursedDependencies
if eachDependency[0] == "'"
deQuotedDep = eachDependency.substring(1)
originalUserSymbol = usr_symbol(deQuotedDep)
newUserSymbol = usr_symbol("AVOID_BINDING_TO_EXTERNAL_SCOPE_VALUE"+deQuotedDep)
replacementsFrom.push originalUserSymbol
replacementsTo.push newUserSymbol
push(originalUserSymbol)
push(newUserSymbol)
subst()
if DEBUG then console.log "after substitution: " + stack[tos-1]
try
simplifyForCodeGeneration()
catch error
if PRINTOUTRESULT then console.log error
errorMessage = error + ""
#debugger
init()
for indexOfEachReplacement in [0...replacementsFrom.length]
#console.log "replacing back " + replacementsTo[indexOfEachReplacement] + " into: " + replacementsFrom[indexOfEachReplacement]
push(replacementsTo[indexOfEachReplacement])
push(replacementsFrom[indexOfEachReplacement])
subst()
clearRenamedVariablesToAvoidBindingToExternalScope()
if errorMessage == ""
toBePrinted = pop()
# we have to get all the variables used on the right side
# here. I.e. to print the arguments it's better to look at the
# actual method body after simplification.
userVariablesMentioned = []
collectUserSymbols(toBePrinted, userVariablesMentioned)
allReturnedPlainStrings = ""
allReturnedLatexStrings = ""
codeGen = true
generatedBody = toBePrinted.toString()
codeGen = false
origPrintMode = printMode
printMode = PRINTMODE_LATEX
bodyForReadableSummaryOfGeneratedCode = toBePrinted.toString()
printMode = origPrintMode
if variablesWithCycles.indexOf(key) != -1
generatedCode += "// " + key + " is part of a cyclic dependency, no code generated."
readableSummaryOfGeneratedCode += "#" + key + " is part of a cyclic dependency, no code generated."
else
###
# using this paragraph instead of the following one
# creates methods signatures that
# are slightly less efficient
# i.e. variables compare even if they are
# simplified away.
# In theory these signatures are more stable, but
# in practice signatures vary quite a bit anyways
# depending on previous assignments for example,
# so it's unclear whether going for stability
# is sensible at all..
if recursedDependencies.length != 0
parameters = "("
for i in recursedDependencies
if i.indexOf("'") != 0
parameters += i + ", "
else
if recursedDependencies.indexOf(i.substring(1)) == -1
parameters += i.substring(1) + ", "
###
# remove all native functions from the
# parameters as well.
userVariablesMentioned = userVariablesMentioned.filter (x) ->
predefinedSymbolsInGlobalScope_doNotTrackInDependencies.indexOf(x + "") == -1
# remove the variable that are not in the dependency list
# i.e. only allow the variables that are in the dependency list
userVariablesMentioned = userVariablesMentioned.filter (x) ->
recursedDependencies.indexOf(x + "") != -1 or
recursedDependencies.indexOf("\'" + x + "") != -1
if userVariablesMentioned.length != 0
parameters = "("
for i in userVariablesMentioned
if i.printname != key
parameters += i.printname + ", "
# eliminate the last ", " for printout clarity
parameters = parameters.replace /, $/gm , ""
parameters += ")"
generatedCode += key + " = function " + parameters + " { return ( " + generatedBody + " ); }"
readableSummaryOfGeneratedCode += key + parameters + " = " + bodyForReadableSummaryOfGeneratedCode
else
generatedCode += key + " = " + generatedBody + ";"
readableSummaryOfGeneratedCode += key + " = " + bodyForReadableSummaryOfGeneratedCode
generatedCode += "\n"
readableSummaryOfGeneratedCode += "\n"
if DEBUG then console.log " " + generatedCode
# eliminate the last new line
generatedCode = generatedCode.replace /\n$/gm , ""
readableSummaryOfGeneratedCode = readableSummaryOfGeneratedCode.replace /\n$/gm , ""
# cleanup
symbolsDependencies = {}
symbolsHavingReassignments = []
patternHasBeenFound = false
symbolsInExpressionsWithoutAssignments = []
if DEBUG then console.log "testable string: " + testableString
if TIMING_DEBUGS
console.log "findDependenciesInScript time for: " + stringToBeRun + " : "+ ((new Date().getTime()) - timeStartFromAlgebra) + "ms"
if ENABLE_CACHING and stringToBeRun != "clearall" and errorMessage == ""
frozen = freeze()
toBeFrozen = [
frozen[0],
frozen[1],
frozen[2],
frozen[3],
frozen[4],
frozen[5],
(new Date().getTime() - timeStartFromAlgebra),
testableString,
scriptEvaluation[0],
generatedCode,
readableSummaryOfGeneratedCode,
scriptEvaluation[1],
errorMessage,
dependencyInfo
]
if CACHE_DEBUGS then console.log "setting cached_findDependenciesInScript on key: " + cacheKey
cached_findDependenciesInScript.set(cacheKey, toBeFrozen)
return [testableString, scriptEvaluation[0], generatedCode, readableSummaryOfGeneratedCode, scriptEvaluation[1], errorMessage, dependencyInfo]
recursiveDependencies = (variableToBeChecked, arrayWhereDependenciesWillBeAdded, variablesAlreadyFleshedOut, variablesWithCycles, chainBeingChecked, cyclesDescriptions) ->
variablesAlreadyFleshedOut.push variableToBeChecked
# recursive dependencies can only be descended if the variable is not bound to a parameter
if symbolsDependencies[chainBeingChecked[chainBeingChecked.length-1]]?
if symbolsDependencies[chainBeingChecked[chainBeingChecked.length-1]].indexOf("'"+variableToBeChecked) != -1
if DEBUG then console.log "can't keep following the chain of " + variableToBeChecked + " because it's actually a variable bound to a parameter"
if arrayWhereDependenciesWillBeAdded.indexOf("'"+variableToBeChecked) == -1 and arrayWhereDependenciesWillBeAdded.indexOf(variableToBeChecked) == -1
arrayWhereDependenciesWillBeAdded.push variableToBeChecked
return arrayWhereDependenciesWillBeAdded
chainBeingChecked.push variableToBeChecked
if !symbolsDependencies[variableToBeChecked]?
# end case: the passed variable has no dependencies
# so there is nothing else to do
if arrayWhereDependenciesWillBeAdded.indexOf(variableToBeChecked) == -1
arrayWhereDependenciesWillBeAdded.push variableToBeChecked
return arrayWhereDependenciesWillBeAdded
else
# recursion case: we have to dig deeper
for i in symbolsDependencies[variableToBeChecked]
# check that there is no recursion in dependencies
# we do that by keeping a list of variables that
# have already been "fleshed-out". If we encounter
# any of those "fleshed-out" variables while
# fleshing out, then there is a cycle
if chainBeingChecked.indexOf(i) != -1
if DEBUG then console.log " found cycle:"
cyclesDescription = ""
for k in chainBeingChecked
if variablesWithCycles.indexOf(k) == -1
variablesWithCycles.push k
if DEBUG then console.log k + " --> "
cyclesDescription += k + " --> "
if DEBUG then console.log " ... then " + i + " again"
cyclesDescription += " ... then " + i + " again"
cyclesDescriptions.push cyclesDescription
#if DEBUG then console.log " --> cycle through " + i
# we want to flesh-out i but it's already been
# fleshed-out, just add it to the variables
# with cycles and move on
# todo refactor this, there are two copies of these two lines
if variablesWithCycles.indexOf(i) == -1
variablesWithCycles.push i
else
# flesh-out i recursively
recursiveDependencies i, arrayWhereDependenciesWillBeAdded, variablesAlreadyFleshedOut, variablesWithCycles, chainBeingChecked, cyclesDescriptions
chainBeingChecked.pop()
#variablesAlreadyFleshedOut.pop()
return arrayWhereDependenciesWillBeAdded
# parses and runs one statement/expression at a time
inited = false
latexErrorSign = "\\rlap{\\large\\color{red}\\bigtriangleup}{\\ \\ \\tiny\\color{red}!}"
turnErrorMessageToLatex = (theErrorMessage) ->
theErrorMessage = theErrorMessage.replace(/\n/g,"")
theErrorMessage = theErrorMessage.replace(/_/g, "} \\_ \\text{");
theErrorMessage = theErrorMessage.replace(new RegExp(String.fromCharCode(transpose_unicode), 'g'), "}{}^{T}\\text{");
theErrorMessage = theErrorMessage.replace(new RegExp(String.fromCharCode(dotprod_unicode), 'g'),"}\\cdot \\text{");
theErrorMessage = theErrorMessage.replace("Stop:","} \\quad \\text{Stop:");
theErrorMessage = theErrorMessage.replace("->","} \\rightarrow \\text{");
theErrorMessage = theErrorMessage.replace("?","}\\enspace " + latexErrorSign + " \\enspace \\text{");
theErrorMessage = "$$\\text{" + theErrorMessage.replace(/\n/g,"") + "}$$"
#console.log "theErrorMessage: " + theErrorMessage
return theErrorMessage
# there are around a dozen different unicodes that
# represent some sort of middle dot, let's catch the most
# common and turn them into what we can process
normaliseDots = (stringToNormalise) ->
stringToNormalise = stringToNormalise.replace(new RegExp(String.fromCharCode(8901), 'g'), String.fromCharCode(dotprod_unicode));
stringToNormalise = stringToNormalise.replace(new RegExp(String.fromCharCode(8226), 'g'), String.fromCharCode(dotprod_unicode));
stringToNormalise = stringToNormalise.replace(new RegExp(String.fromCharCode(12539), 'g'), String.fromCharCode(dotprod_unicode));
stringToNormalise = stringToNormalise.replace(new RegExp(String.fromCharCode(55296), 'g'), String.fromCharCode(dotprod_unicode));
stringToNormalise = stringToNormalise.replace(new RegExp(String.fromCharCode(65381), 'g'), String.fromCharCode(dotprod_unicode));
return stringToNormalise
CACHE_DEBUGS = false
CACHE_HITSMISS_DEBUGS = false
TIMING_DEBUGS = false
cacheMissPenalty = 0
run = (stringToBeRun, generateLatex = false) ->
timeStart = new Date().getTime()
#stringToBeRun = stringToBeRun + "\n"
stringToBeRun = normaliseDots stringToBeRun
#console.log "run running: " + stringToBeRun
if ENABLE_CACHING and stringToBeRun != "clearall"
currentStateHash = getStateHash()
cacheKey = currentStateHash + " stringToBeRun: " + stringToBeRun
if CACHE_DEBUGS then console.log "cached_runs key: " + cacheKey
possiblyCached = cached_runs.get(cacheKey)
#possiblyCached = null
if possiblyCached?
if CACHE_HITSMISS_DEBUGS then console.log "cached_runs hit on: " + stringToBeRun
unfreeze(possiblyCached)
# return the output string
if TIMING_DEBUGS
totalTime = new Date().getTime() - timeStart
console.log "run time: " + totalTime + "ms, saved " + (possiblyCached[possiblyCached.length-2] - totalTime) + "ms due to cache hit"
return possiblyCached[possiblyCached.length - 1]
else
if CACHE_HITSMISS_DEBUGS then console.log "cached_runs miss on: " + stringToBeRun
if TIMING_DEBUGS
cacheMissPenalty = (new Date().getTime() - timeStart)
if stringToBeRun == "selftest"
selftest()
return
#if (setjmp(stop_return))
# return
if !inited
inited = true
init()
i = 0
n = 0
indexOfPartRemainingToBeParsed = 0
allReturnedPlainStrings = ""
allReturnedLatexStrings = ""
while (1)
# while we can keep scanning commands out of the
# passed input AND we can execute them...
try
errorMessage = ""
check_stack()
n = scan(stringToBeRun.substring(indexOfPartRemainingToBeParsed))
p1 = pop()
check_stack()
catch error
if PRINTOUTRESULT then console.log error
#debugger
allReturnedPlainStrings += error.message
if generateLatex
#debugger
theErrorMessage = turnErrorMessageToLatex error.message
allReturnedLatexStrings += theErrorMessage
reset_after_error()
break
if (n == 0)
break
# if debug mode then print the source text
#if (equaln(get_binding(symbol(TRACE)), 1)) {
# for (i = 0 i < n i++)
# if (s[i] != '\r')
# printchar(s[i])
# if (s[n - 1] != '\n') # n is not zero, see above
# printchar('\n')
#}
indexOfPartRemainingToBeParsed += n
push(p1)
#debugger
errorWhileExecution = false
try
stringsEmittedByUserPrintouts = ""
top_level_eval()
#console.log "emitted string after top_level_eval(): >" + stringsEmittedByUserPrintouts + "<"
#console.log "allReturnedPlainStrings string after top_level_eval(): >" + allReturnedPlainStrings + "<"
p2 = pop()
check_stack()
if (isstr(p2))
if DEBUG then console.log(p2.str)
if DEBUG then console.log("\n")
# if the return value is nil there isn't much point
# in adding "nil" to the printout
if (p2 == symbol(NIL))
#collectedPlainResult = stringsEmittedByUserPrintouts
collectedPlainResult = stringsEmittedByUserPrintouts
if generateLatex
collectedLatexResult = "$$" + stringsEmittedByUserPrintouts + "$$"
else
#console.log "emitted string before collectPlainStringFromReturnValue: >" + stringsEmittedByUserPrintouts + "<"
#console.log "allReturnedPlainStrings string before collectPlainStringFromReturnValue: >" + allReturnedPlainStrings + "<"
collectedPlainResult = print_expr(p2)
collectedPlainResult += "\n"
#console.log "collectedPlainResult: >" + collectedPlainResult + "<"
if generateLatex
collectedLatexResult = "$$" + collectLatexStringFromReturnValue(p2) + "$$"
if DEBUG then console.log "collectedLatexResult: " + collectedLatexResult
allReturnedPlainStrings += collectedPlainResult
if generateLatex then allReturnedLatexStrings += collectedLatexResult
if PRINTOUTRESULT
if DEBUG then console.log "printline"
if DEBUG then console.log collectedPlainResult
#alert collectedPlainResult
if PRINTOUTRESULT
if DEBUG then console.log "display:"
print2dascii(p2)
if generateLatex then allReturnedLatexStrings += "\n"
catch error
errorWhileExecution = true
collectedPlainResult = error.message
if generateLatex then collectedLatexResult = turnErrorMessageToLatex error.message
if PRINTOUTRESULT then console.log collectedPlainResult
allReturnedPlainStrings += collectedPlainResult
if collectedPlainResult != ""
allReturnedPlainStrings += "\n"
if generateLatex
allReturnedLatexStrings += collectedLatexResult
allReturnedLatexStrings += "\n"
resetCache()
init()
if allReturnedPlainStrings[allReturnedPlainStrings.length-1] == "\n"
allReturnedPlainStrings = allReturnedPlainStrings.substring(0,allReturnedPlainStrings.length-1)
if generateLatex
if allReturnedLatexStrings[allReturnedLatexStrings.length-1] == "\n"
allReturnedLatexStrings = allReturnedLatexStrings.substring(0,allReturnedLatexStrings.length-1)
if generateLatex
if DEBUG then console.log "allReturnedLatexStrings: " + allReturnedLatexStrings
# TODO handle this case of caching
stringToBeReturned = [allReturnedPlainStrings, allReturnedLatexStrings]
else
stringToBeReturned = allReturnedPlainStrings
if ENABLE_CACHING and stringToBeRun != "clearall" and !errorWhileExecution
frozen = freeze()
toBeFrozen = [frozen[0], frozen[1], frozen[2], frozen[3], frozen[4], frozen[5], (new Date().getTime() - timeStart), stringToBeReturned]
if CACHE_DEBUGS then console.log "setting cached_runs on key: " + cacheKey
cached_runs.set(cacheKey, toBeFrozen)
if TIMING_DEBUGS
timingDebugWrite = "run time on: " + stringToBeRun + " : " + (new Date().getTime() - timeStart) + "ms"
if ENABLE_CACHING and stringToBeRun != "clearall" then timingDebugWrite += ", of which cache miss penalty: " + cacheMissPenalty + "ms"
console.log timingDebugWrite
allReturnedPlainStrings = ""
allReturnedLatexStrings = ""
return stringToBeReturned
check_stack = ->
if (tos != 0)
debugger
stop("stack error")
if (frame != TOS)
debugger
stop("frame error")
if chainOfUserSymbolsNotFunctionsBeingEvaluated.length != 0
debugger
stop("symbols evaluation still ongoing?")
if evaluatingAsFloats != 0
debugger
stop("numeric evaluation still ongoing?")
if evaluatingPolar != 0
debugger
stop("evaluation of polar still ongoing?")
# cannot reference symbols yet
# returns nil on stack if no result to print
top_level_eval = ->
if DEBUG then console.log "#### top level eval"
save()
trigmode = 0
p1 = symbol(AUTOEXPAND)
if (iszero(get_binding(p1)))
expanding = 0
else
expanding = 1
p1 = pop()
push(p1)
Eval()
p2 = pop()
# "draw", "for" and "setq" return "nil", there is no result to print
if (p2 == symbol(NIL))
push(p2)
restore()
return
# update "last" to contain the last result
set_binding(symbol(LAST), p2)
if (!iszero(get_binding(symbol(BAKE))))
push(p2)
bake()
p2 = pop()
# If we evaluated the symbol "i" or "j" and the result was sqrt(-1)
# then don't do anything.
# Otherwise if "j" is an imaginary unit then subst.
# Otherwise if "i" is an imaginary unit then subst.
if ((p1 == symbol(SYMBOL_I) || p1 == symbol(SYMBOL_J)) && isimaginaryunit(p2))
doNothing = 0
else if (isimaginaryunit(get_binding(symbol(SYMBOL_J))))
push(p2)
push(imaginaryunit)
push_symbol(SYMBOL_J)
subst()
p2 = pop()
else if (isimaginaryunit(get_binding(symbol(SYMBOL_I))))
push(p2)
push(imaginaryunit)
push_symbol(SYMBOL_I)
subst()
p2 = pop()
#ifndef LINUX ----------------------
# if we evaluated the symbol "a" and got "b" then print "a=b"
# do not print "a=a"
#if (issymbol(p1) && !iskeyword(p1) && p1 != p2 && test_flag == 0)
# push_symbol(SETQ)
# push(p1)
# push(p2)
# list(3)
# p2 = pop()
#endif -----------------------------
push(p2)
restore()
check_esc_flag = ->
if (esc_flag)
stop("esc key")
# this is called when the whole notebook is re-run
# so we get the chance of clearing the whole state from
# scratch.
# In practice, the state we need to clear that persists
# across blocks are only the patterns, so
# just eject those.
clearAlgebraEnvironment = ->
#console.log "CLEARING clearAlgebraEnvironment ============================================================="
do_clearall()
computeDependenciesFromAlgebra = (codeFromAlgebraBlock) ->
if DEBUG then console.log "computeDependenciesFromAlgebra!!!"
# return findDependenciesInScript(codeFromAlgebraBlock, true)[6]
# TODO this part below is duplicated from computeResultsAndJavaScriptFromAlgebra
# ...should refactor.
originalcodeFromAlgebraBlock = codeFromAlgebraBlock
keepState = true
called_from_Algebra_block = true
#console.log "codeFromAlgebraBlock: " + codeFromAlgebraBlock
codeFromAlgebraBlock = normaliseDots codeFromAlgebraBlock
if !keepState
userSimplificationsInListForm = []
userSimplificationsInProgramForm = ""
for i in userSimplificationsInListForm
#console.log "silentpattern(" + car(i) + ","+cdr(i)+")"
userSimplificationsInProgramForm += "silentpattern(" + car(i) + ","+ car(cdr(i)) + "," + car(cdr(cdr(i))) + ")\n"
do_clearall()
codeFromAlgebraBlock = userSimplificationsInProgramForm + codeFromAlgebraBlock
if DEBUG then console.log "codeFromAlgebraBlock including patterns: " + codeFromAlgebraBlock
if DEBUG
console.log "computeDependenciesFromAlgebra: patterns in the list --------------- "
for i in userSimplificationsInListForm
console.log car(i) + ","+cdr(i)+")"
console.log "...end of list --------------- "
called_from_Algebra_block = false
return findDependenciesInScript(codeFromAlgebraBlock, true)[6]
computeResultsAndJavaScriptFromAlgebra = (codeFromAlgebraBlock) ->
originalcodeFromAlgebraBlock = codeFromAlgebraBlock
keepState = true
called_from_Algebra_block = true
timeStartFromAlgebra = new Date().getTime()
if TIMING_DEBUGS
console.log " --------- computeResultsAndJavaScriptFromAlgebra input: " + codeFromAlgebraBlock + " at: " + (new Date())
# we start "clean" each time:
# clear all the symbols and then re-define
# the "starting" symbols.
#console.log "codeFromAlgebraBlock: " + codeFromAlgebraBlock
codeFromAlgebraBlock = normaliseDots codeFromAlgebraBlock
stringToBeRun = codeFromAlgebraBlock
if DEBUG
console.log "computeResultsAndJavaScriptFromAlgebra: patterns in the list --------------- "
for i in userSimplificationsInListForm
console.log car(i) + ","+cdr(i)+")"
console.log "...end of list --------------- "
if !keepState
userSimplificationsInListForm = []
userSimplificationsInProgramForm = ""
for i in userSimplificationsInListForm
#console.log "silentpattern(" + car(i) + ","+cdr(i)+")"
userSimplificationsInProgramForm += "silentpattern(" + car(i) + ","+ car(cdr(i)) + "," + car(cdr(cdr(i))) + ")\n"
do_clearall()
codeFromAlgebraBlock = userSimplificationsInProgramForm + codeFromAlgebraBlock
if DEBUG then console.log "codeFromAlgebraBlock including patterns: " + codeFromAlgebraBlock
#debugger
[testableStringIsIgnoredHere,result,code,readableSummaryOfCode, latexResult, errorMessage, dependencyInfo] =
findDependenciesInScript(codeFromAlgebraBlock)
called_from_Algebra_block = false
if readableSummaryOfCode != "" or errorMessage != ""
result += "\n" + readableSummaryOfCode
if errorMessage != ""
result += "\n" + errorMessage
result = result.replace /\n/g,"\n\n"
latexResult += "\n" + "$$" + readableSummaryOfCode + "$$"
if errorMessage != ""
latexResult += turnErrorMessageToLatex errorMessage
latexResult = latexResult.replace /\n/g,"\n\n"
# remove empty results altogether from latex output, which happens
# for example for assignments to variables or
# functions definitions
latexResult = latexResult.replace /\n*/,""
latexResult = latexResult.replace /\$\$\$\$\n*/g,""
code = code.replace /Math\./g,""
code = code.replace /\n/g,"\n\n"
#console.log "code: " + code
#console.log "result: " + result
#console.log "latexResult: " + latexResult
if TIMING_DEBUGS
console.log "computeResultsAndJavaScriptFromAlgebra time (total time from notebook and back) for: " + stringToBeRun + " : "+ ((new Date().getTime()) - timeStartFromAlgebra) + "ms"
#code: "// no code generated yet\n//try again later"
#code: "console.log('some passed code is run'); window.something = 1;"
code: code
# TODO temporarily pass latex in place of standard result too
result: latexResult
latexResult: latexResult
dependencyInfo: dependencyInfo
enableCaching = ->
ENABLE_CACHING = true
disableCaching = ->
ENABLE_CACHING = false
(exports ? this).run = run
(exports ? this).findDependenciesInScript = findDependenciesInScript
(exports ? this).computeDependenciesFromAlgebra = computeDependenciesFromAlgebra
(exports ? this).computeResultsAndJavaScriptFromAlgebra = computeResultsAndJavaScriptFromAlgebra
(exports ? this).clearAlgebraEnvironment = clearAlgebraEnvironment
(exports ? this).enableCaching = enableCaching
(exports ? this).disableCaching = disableCaching