Skip to content

Commit 7328dd6

Browse files
all added pmbash language features are now interpreted by the Interpreter class
1 parent ce4493c commit 7328dd6

File tree

1 file changed

+211
-59
lines changed

1 file changed

+211
-59
lines changed

src/pman/format/pmsh/Interpreter.hx

Lines changed: 211 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -2,22 +2,25 @@ package pman.format.pmsh;
22

33
import tannus.io.*;
44
import tannus.ds.*;
5+
import tannus.async.*;
56
import tannus.TSys as Sys;
67

7-
import pman.async.*;
8+
import pman.format.pmsh.NewParser;
89
import pman.format.pmsh.Token;
910
import pman.format.pmsh.Expr;
1011
import pman.format.pmsh.Cmd;
1112

12-
import electron.Tools.*;
13+
import edis.Globals.*;
14+
import pman.Globals.*;
1315
import Slambda.fn;
1416

1517
using StringTools;
1618
using tannus.ds.StringUtils;
1719
using Lambda;
1820
using tannus.ds.ArrayTools;
1921
using Slambda;
20-
using pman.async.VoidAsyncs;
22+
using tannus.async.Asyncs;
23+
using pman.format.pmsh.ExprTools;
2124

2225
class Interpreter {
2326
/* Constructor Function */
@@ -92,7 +95,7 @@ class Interpreter {
9295
* execute the given expression
9396
*/
9497
public function execute(e:Expr, complete:VoidCb):Void {
95-
async( e )( complete );
98+
eval(e, complete);
9699
}
97100

98101
/**
@@ -106,64 +109,147 @@ class Interpreter {
106109
}
107110

108111
/**
109-
* build the given expression out into a VoidAsync
110-
*/
111-
private function async(expr : Expr):VoidAsync {
112-
switch ( expr ) {
113-
/* multiple expressions in a block */
114-
case EBlock( body ):
115-
return ((body.map( async )).series.bind());
116-
117-
/* variable assignment */
112+
evaluate the given expression, and obtain its return-value
113+
**/
114+
function eval(expr:Expr, complete:VoidCb):Promise<ExprReturn> {
115+
switch expr {
116+
/* evaluate a word-expression */
117+
case EWord(word):
118+
//evalWord(word, complete);
119+
complete();
120+
121+
/* evaluate a variable assignment */
118122
case ESetVar(name, value):
119-
return function(done:VoidCb):Void {
120-
var sname = wordToString( name );
121-
var svalue = wordToString( value );
122-
environment[sname] = svalue;
123-
done();
124-
};
123+
environment[wordToString(name)] = wordToString(value);
124+
complete();
125+
126+
/* binary operator */
127+
case EBinaryOperator(op, leftExpr, rightExpr):
128+
evalBinop(op, leftExpr, rightExpr, complete);
129+
130+
/* unary operator */
131+
case EUnaryOperator(op, expr):
132+
throw EUnexpected( expr );
133+
134+
/* block expression */
135+
case EBlock(exprs), ERoot(exprs):
136+
exprs.map(e -> (next -> eval(e, next))).series( complete );
125137

126138
/* basic command */
127-
case ECommand(nameWord, params):
128-
var name = wordToString( nameWord );
129-
return function(done : VoidCb):Void {
130-
expand(params, function(?error, ?paramWords) {
131-
if (error != null) {
132-
done( error );
133-
}
134-
else {
135-
resolve(name, function(?error, ?command) {
136-
if (error != null) {
137-
done( error );
138-
}
139-
else {
140-
if (command != null) {
141-
resolveArgValues(paramWords, function(?error, ?args) {
142-
if (error != null)
143-
done( error );
144-
else {
145-
var commandArgs = paramWords.zipmap(args, fn([word, value] => new CmdArg(EWord(word), value)));
146-
command.execute(this, commandArgs, done.wrap(function(_, ?err) {
147-
trace('command exited');
148-
_( err );
149-
}));
150-
}
151-
});
152-
}
153-
else {
154-
done('Error: No command "$name" found');
155-
}
156-
}
157-
});
158-
}
159-
});
160-
};
139+
case ECommand(cmd):
140+
evalCmd(cmd, complete);
161141

162-
default:
163-
throw 'Error: Unexpected $expr';
142+
/* function declaration */
143+
case EFunc(name, expr):
144+
commands[name] = new FuncCmd(this, name, expr);
145+
complete();
146+
147+
/* for-loop statement */
148+
case EFor(id, iter, expr):
149+
complete();
150+
}
151+
152+
return Promise.resolve(ERVoid);
153+
}
154+
155+
function evalBinop(op:Binop, left:Expr, right:Expr, complete:VoidCb) {
156+
switch op {
157+
case OpAnd:
158+
evalAndOp(left, right, complete);
159+
160+
case OpOr:
161+
evalOrOp(left, right, complete);
162+
163+
case OpPipe:
164+
evalPipeOp(left, right, complete);
165+
166+
case _:
167+
throw PmShError.EUnexpected(op);
164168
}
165169
}
166170

171+
function evalPipeOp(left:Expr, right:Expr, complete:VoidCb) {
172+
eval(left, function(?error) {
173+
if (error != null)
174+
complete( error );
175+
else {
176+
trace('TODO: Piping not yet working');
177+
eval(right, complete);
178+
}
179+
});
180+
}
181+
182+
function evalAndOp(left:Expr, right:Expr, complete:VoidCb) {
183+
eval(left, function(?error) {
184+
if (error != null)
185+
complete( error );
186+
else {
187+
eval(right, complete);
188+
}
189+
});
190+
}
191+
192+
function evalOrOp(left:Expr, right:Expr, complete:VoidCb) {
193+
eval(left, function(?error) {
194+
if (error == null)
195+
complete();
196+
else {
197+
eval(right, complete);
198+
}
199+
});
200+
}
201+
202+
/**
203+
evaluate a Command expression
204+
**/
205+
function evalCmd(cmd:CommandExpr, complete:VoidCb) {
206+
var name = resolve.bind(wordToString(cmd.command), _).toPromise();
207+
name.then(function(cmd: Cmd) {
208+
this.currentCmd = cmd;
209+
});
210+
var pwp:Promise<Array<Word>> = expand.bind(cmd.parameters, _).toPromise();
211+
var pwvp:Promise<Array<Dynamic>> = pwp.derive(function(p, resolve, reject) {
212+
p.then(function(words) {
213+
resolveArgValues(words, function(?error, ?values) {
214+
if (error != null) {
215+
reject( error );
216+
}
217+
else {
218+
resolve( values );
219+
}
220+
});
221+
}, reject);
222+
});
223+
var pwpp:Promise<Pair<Array<Word>, Array<Dynamic>>> = Promise.pair(new Pair(pwvp, pwp));
224+
var cap:Promise<Array<CmdArg>> = pwpp.transform(function(pair) {
225+
return pair.left.zipmap(pair.right, fn([word, value] => new CmdArg(EWord(word), value)));
226+
});
227+
var cmdt:Promise<Pair<Cmd, Array<CmdArg>>> = Promise.pair(new Pair(cap, name));
228+
var cmdExpr:CommandExpr = cmd;
229+
cmdt.then(function(pair) {
230+
var cmd:Null<Cmd> = pair.left,
231+
argv:Array<CmdArg> = pair.right;
232+
233+
if (cmd == null) {
234+
return complete(ECommandNotFound( cmdExpr.command ));
235+
}
236+
else {
237+
cmd.execute(this, argv, complete.wrap(function(_, ?error) {
238+
_(error);
239+
240+
currentCmd = null;
241+
}));
242+
}
243+
}, complete.raise());
244+
}
245+
246+
/**
247+
* build the given expression out into a VoidAsync
248+
*/
249+
private function async(expr : Expr):VoidAsync {
250+
return eval.bind(expr, _);
251+
}
252+
167253
/**
168254
* resolve a Cmd from name
169255
*/
@@ -229,23 +315,65 @@ class Interpreter {
229315
switch ( word ) {
230316
case Ident(s), String(s, _):
231317
return s;
318+
232319
case Ref(name):
233320
if (environment.exists( name )) {
234321
return environment[name];
235322
}
236323
else {
237324
return '';
238325
}
326+
327+
case Interpolate(e):
328+
throw EWhatTheFuck('aww, poor design, sha', e);
329+
330+
case Substitution(type, name, value):
331+
return wordToString(Ref(name));
239332
}
240333
}
241334

335+
/**
336+
obtain a value from a Word instance
337+
**/
338+
function evalWord(word: Word):Promise<Dynamic> {
339+
return new Promise(function(resolve, reject) {
340+
switch word {
341+
case Ident(ident):
342+
return resolve( ident );
343+
344+
case String(str, delimiter):
345+
switch delimiter {
346+
case 0, 1:
347+
return resolve( str );
348+
349+
case _:
350+
return reject(EUnexpected( delimiter ));
351+
}
352+
353+
case Ref(name):
354+
return resolve(getenv(name));
355+
356+
case Interpolate(e):
357+
return reject(EWhatTheFuck('command return-values need to be implemented before interpolation can be', e));
358+
359+
case Substitution(type, name, valueExpr):
360+
return resolve(untyped evalSubstitution(type, evalWord(Ident(name)), valueExpr));
361+
}
362+
});
363+
}
364+
365+
function evalSubstitution(type:SubstitutionType, left:Promise<Dynamic>, right:Expr):Promise<Dynamic> {
366+
trace('TODO: properly implement value substitution in pmbash');
367+
return left;
368+
}
369+
242370
/**
243371
* creates a 'partial'
244372
*/
245373
public function createPartialFromExpr(e : Expr):PmshPartial {
246374
switch ( e ) {
247-
case ECommand(nameWord, params), EBlock([ECommand(nameWord, params)]):
248-
return new PmshPartial(nameWord, params);
375+
case ECommand(cmd), EBlock([ECommand(cmd)]), ERoot([ECommand(cmd)]):
376+
return new PmshPartial(cmd.command, cmd.parameters);
249377

250378
default:
251379
throw 'TypeError: partials may only be created from command-invokation expressions';
@@ -256,15 +384,20 @@ class Interpreter {
256384
* create a partial from a String
257385
*/
258386
public function parsePartial(exprString : String):PmshPartial {
259-
return createPartialFromExpr(Parser.runString( exprString ));
387+
return createPartialFromExpr(NewParser.runString( exprString ));
260388
}
261389

262390
/* === Instance Fields === */
263391

264392
public var commands : Map<String, Cmd>;
265393
public var environment : Dict<String, String>;
394+
395+
var currentCmd: Null<Cmd> = null;
266396
}
267397

398+
/**
399+
betty
400+
**/
268401
@:access( pman.format.pmsh.Interpreter )
269402
@:structInit
270403
class PmshPartial {
@@ -283,7 +416,7 @@ class PmshPartial {
283416
var fullParams = params;
284417
if (additionalParams != null)
285418
fullParams = fullParams.concat( additionalParams );
286-
return ECommand(cmd, fullParams);
419+
return ECommand(new CommandExpr(cmd, fullParams));
287420
}
288421

289422
/**
@@ -337,3 +470,22 @@ class PmshPartial {
337470
});
338471
}
339472
}
473+
474+
/**
475+
values that evaluating an expression can yield
476+
**/
477+
enum ExprReturn {
478+
// no return value
479+
ERVoid;
480+
481+
// the 'return' value from executing a command
482+
ERCommand(out: CmdReturn<Dynamic, Dynamic>);
483+
484+
// the 'return' value from evaluating a word-expression
485+
ERWord(wval: EValue<Dynamic>);
486+
}
487+
488+
enum CmdReturn<Val, Err> {
489+
CRReturn(value:EValue<Val>, error:Err):CmdReturn<Val, Err>;
490+
CROutput(stdOut:ByteArray, stdErr:ByteArray):CmdReturn<ByteArray, ByteArray>;
491+
}

0 commit comments

Comments
 (0)