|
|
|
|
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title></title>
<script type="text/javascript">
var globalEnvironment = {
't': true,
'nil': false,
'+': function(vargs) {
return operator( function(left, right) {
return left + right;
}, vargs );
},
'-': function(vargs) {
return operator( function(left, right) {
return left - right;
}, vargs );
},
'*': function(vargs) {
return operator( function(left, right) {
return left * right;
}, vargs );
},
'/': function(vargs) {
return operator( function(left, right) {
return left / right;
}, vargs );
}
}
function operator(cb, vargs) {
if (vargs.length < 2) {
throw 'требуется минимум 2 аргумента';
}
var i = 0, result = vargs[0];
while (1) {
if ( ++i == vargs.length ) {
break;
}
result = cb( result, vargs[i] );
}
return result;
}
var parse = function(input) {
// в js нет просмотра назад хз как всё описать с помощью одной регулярки
var tokens = /;.*|"((?:[^"\\]|\\[\s\S])*)"|([^\s`();'",]{2,}|[^\s`();'",.])|(\S)/g,
tok,
expArr = [];
function readToken() {
var tok, i;
while ( tok = tokens.exec(input) ) {
for (i = 1; i < tok.length; ++i) {
if ( tok[i] ) {
return tok;
}
}
}
}
function parseToken(tok) {
if ( tok[1] ) {
return [ 'string', tok[1].replace( /\\"/g, '"') ];
}
if ( tok[2] ) {
if (tok[2] < 0 || tok[2] == 0 || tok[2] > 0) {
return [ 'number', tok[2] * 1 ];
}
return [ 'word', tok[2].toLowerCase() ];
}
if (tok[3] == '(') {
var arr = [ 'list', [] ];
while ( tok = readToken() ) {
if (tok[3] == ')') {
return arr;
}
arr[1].push( parseToken(tok) );
}
throw 'Возможно, пропущена )';
}
throw 'Неожиданный токен ' + tok[3] + ' на позиции ' + tok.index;
}
while ( tok = readToken() ) {
expArr.push( parseToken(tok) );
}
return expArr;
}
// new Environment( B, new Environment( A ) )
//
// undefined
// \
// A - data
// \
// B - data
//
// Где A, B - узлы. B.parent содержит ссылку на A.
function Environment(data, parent) {
this.data = data;
this.parent = parent;
}
Environment.prototype.lookup = function(id) {
var scope = this, data;
while (scope) {
if (scope.data[id] !== undefined) {
return scope.data[id];
}
// если в текущем scope переменную не нашли смотрим на уровень выше
scope = scope.parent;
}
throw 'Обьект ' + id + ' не найден';
}
function evaluate(exp, env) {
switch ( exp[0] ) {
case 'word':
return env.lookup( exp[1] );
case 'list':
var name;
exp = exp[1];
name = exp[0][1];
exp[0] = evaluate( exp[0], env );
if ( typeof exp[0] == 'function' ) {
for (var i = 1; i < exp.length; ++i) {
exp[i] = evaluate( exp[i], env )
}
try {
return exp[0].call(null, exp.slice(1), env);
}
catch (err) {
throw 'Ошибка в функции ' + name + ': ' + err;
}
}
throw '"' + name + '" не является функцией';
default:
return exp[1];
}
}
function execute(input) {
var expArr = parse(input),
env = new Environment( {}, new Environment( globalEnvironment ) );
alert( 'Подобие байт-кода:\n\n' + JSON.stringify(expArr) );
for (var i = 0; i < expArr.length; ++i) {
alert( evaluate(expArr[i], env) );
}
}
// Ссылки:
//
// http://ru.wikipedia.org/wiki/Лексический_анализ
// http://ru.wikipedia.org/wiki/Интерпретатор_(шаблон_проектирования)
// http://ru.wikipedia.org/wiki/Польская_нотация
// http://en.wikibooks.org/wiki/Common_Lisp/First_steps/Beginner_tutorial
// http://www.csci.csusb.edu/dick/samples/lisp.lexemes.html
var ge = function(id) {
return document.getElementById(id);
}
window.addEventListener( 'load', function() {
ge('execute').onclick = function() {
try {
execute( ge('input').value );
}
catch (err) {
alert(err);
}
}
} );
</script>
</head>
<body>
<textarea id="input"></textarea> <button id="execute">Выполнить!</button>
</body>
<script> alert( document.getElementsByTagName('script')[0].innerText ); </script>
</html>
|
Нужно: setq, defun и циклы по массивам.
Умеем: базовые математические операции, реализована локальная область видимости
Советы нужны любые | |
|
|
|
|
|
|
|
для: Красная_шляпа
(20.12.2012 в 17:44)
| | Эээ... на JavaScript? Вообще lisp пишется на lisp, нужны только 5 базовых функций - все остальное строится восстанавливается из них. В СССР были даже LISP-машины, где в процессоры эти функции были реализованы в виде команд, а сам LISP служил своеобразным ассемблером... Ни в коем случае не критикую, но вы по полезности/эффективности пишите ассемблер на Basic... можно и полезно, но настоящего ассемблера у вас не получится... впрочем настоящий LISP мало где встречается, сплошь и рядом ядро уже предкомпилировано, а это влияет на динамические свойства языка.
PS Вообще возня вокруг LISP - это правильно, весело и здорово, но язык мягко говоря туго подходит для промышленной эксплуатации в современных условиях. | |
|
|
|
|
|
|
|
для: cheops
(20.12.2012 в 21:44)
| | самоцель не воссоздать язык с нуля а написать интерпретатор. лисп самый простой просто язык ну почти поэтому был выбран он. а так можно же любой язык реализовать( только придется использовать обратную польскую нотацию). а вообще я просто на википедии увидел processing.js это детище резига и компании язык со статической типизацией и решеткоподобным синтаксисом. а вообще есть реализации питона руби брейнфака и пр. недавно наткнулся на интерпретатор яваскрипт на самом же яваскрипт. есть интепретатор яваскрипт на питоне (9кб и 36кб) всего 36 кб весь яваскрипт уместился! да лисп древний язык ему 60 лет где то авторы и понятия не имели про ОПН наверное поэтому реализовали польскую нотацию префиксную. занимаясь этим казалось бы бесполезным делом можно понять принципы работы языка все мы говорим по русски но не задумываемся о правилах прежде чем что то сказать. так вот и кодить можно на чем угодно не понимая смысла | |
|
|
|
|
|
|
|
для: Красная_шляпа
(20.12.2012 в 22:20)
| | кодить можно на чем угодно не понимая смысла
этим большая часть интернета забита... называется говнокодить :) | |
|
|
|